Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Hiredis >= 1.x only (remove packaging dependency) #2385

Merged
merged 3 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Fix reusing the old nodes' connections when cluster topology refresh is being done
* Fix RedisCluster to immediately raise AuthenticationError without a retry
* ClusterPipeline Doesn't Handle ConnectionError for Dead Hosts (#2225)
* Remove compatibility code for old versions of Hiredis, drop Packaging dependency

* 4.1.3 (Feb 8, 2022)
* Fix flushdb and flushall (#1926)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ To install redis-py, simply:
$ pip install redis
```

For faster performance, install redis with hiredis support, this provides a compiled response parser, and *for most cases* requires zero code changes. By default, if hiredis is available, redis-py will attempt to use it for response parsing.
For faster performance, install redis with hiredis support, this provides a compiled response parser, and *for most cases* requires zero code changes.
By default, if hiredis >= 1.0 is available, redis-py will attempt to use it for response parsing.

``` bash
$ pip install redis[hiredis]
Expand Down
1 change: 1 addition & 0 deletions dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ flake8==4.0.1
flynt~=0.69.0
isort==5.10.1
mock==4.0.3
packaging>=20.4
pytest==6.2.5
pytest-timeout==2.0.1
pytest-asyncio>=0.16.0
Expand Down
54 changes: 10 additions & 44 deletions redis/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
from time import time
from urllib.parse import parse_qs, unquote, urlparse

from packaging.version import Version

from redis.backoff import NoBackoff
from redis.exceptions import (
AuthenticationError,
Expand Down Expand Up @@ -54,16 +52,6 @@
if HIREDIS_AVAILABLE:
import hiredis

hiredis_version = Version(hiredis.__version__)
HIREDIS_SUPPORTS_CALLABLE_ERRORS = hiredis_version >= Version("0.1.3")
HIREDIS_SUPPORTS_BYTE_BUFFER = hiredis_version >= Version("0.1.4")
HIREDIS_SUPPORTS_ENCODING_ERRORS = hiredis_version >= Version("1.0.0")

HIREDIS_USE_BYTE_BUFFER = True
# only use byte buffer if hiredis supports it
if not HIREDIS_SUPPORTS_BYTE_BUFFER:
HIREDIS_USE_BYTE_BUFFER = False

SYM_STAR = b"*"
SYM_DOLLAR = b"$"
SYM_CRLF = b"\r\n"
Expand Down Expand Up @@ -380,9 +368,7 @@ def __init__(self, socket_read_size):
if not HIREDIS_AVAILABLE:
raise RedisError("Hiredis is not installed")
self.socket_read_size = socket_read_size

if HIREDIS_USE_BYTE_BUFFER:
self._buffer = bytearray(socket_read_size)
self._buffer = bytearray(socket_read_size)

def __del__(self):
try:
Expand All @@ -393,16 +379,14 @@ def __del__(self):
def on_connect(self, connection, **kwargs):
self._sock = connection._sock
self._socket_timeout = connection.socket_timeout
kwargs = {"protocolError": InvalidResponse, "replyError": self.parse_error}

# hiredis < 0.1.3 doesn't support functions that create exceptions
if not HIREDIS_SUPPORTS_CALLABLE_ERRORS:
kwargs["replyError"] = ResponseError
kwargs = {
"protocolError": InvalidResponse,
"replyError": self.parse_error,
"errors": connection.encoder.encoding_errors,
}

if connection.encoder.decode_responses:
kwargs["encoding"] = connection.encoder.encoding
if HIREDIS_SUPPORTS_ENCODING_ERRORS:
kwargs["errors"] = connection.encoder.encoding_errors
self._reader = hiredis.Reader(**kwargs)
self._next_response = False

Expand All @@ -427,17 +411,10 @@ def read_from_socket(self, timeout=SENTINEL, raise_on_timeout=True):
try:
if custom_timeout:
sock.settimeout(timeout)
if HIREDIS_USE_BYTE_BUFFER:
bufflen = self._sock.recv_into(self._buffer)
if bufflen == 0:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
self._reader.feed(self._buffer, 0, bufflen)
else:
buffer = self._sock.recv(self.socket_read_size)
# an empty string indicates the server shutdown the socket
if not isinstance(buffer, bytes) or len(buffer) == 0:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
self._reader.feed(buffer)
bufflen = self._sock.recv_into(self._buffer)
if bufflen == 0:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
self._reader.feed(self._buffer, 0, bufflen)
# data was read from the socket and added to the buffer.
# return True to indicate that data was read.
return True
Expand Down Expand Up @@ -479,17 +456,6 @@ def read_response(self, disable_decoding=False):
response = self._reader.gets(False)
else:
response = self._reader.gets()
# if an older version of hiredis is installed, we need to attempt
# to convert ResponseErrors to their appropriate types.
if not HIREDIS_SUPPORTS_CALLABLE_ERRORS:
if isinstance(response, ResponseError):
response = self.parse_error(response.args[0])
elif (
isinstance(response, list)
and response
and isinstance(response[0], ResponseError)
):
response[0] = self.parse_error(response[0].args[0])
# if the response is a ConnectionError or the response is a list and
# the first item is a ConnectionError, raise it as something bad
# happened
Expand Down
3 changes: 2 additions & 1 deletion redis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
try:
import hiredis # noqa

HIREDIS_AVAILABLE = True
# Only support Hiredis >= 1.0:
HIREDIS_AVAILABLE = not hiredis.__version__.startswith("0.")
except ImportError:
HIREDIS_AVAILABLE = False

Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
async-timeout>=4.0.2
deprecated>=1.2.3
packaging>=20.4
typing-extensions; python_version<"3.8"
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
python_requires=">=3.7",
install_requires=[
"deprecated>=1.2.3",
"packaging>=20.4",
'importlib-metadata >= 1.0; python_version < "3.8"',
'typing-extensions; python_version<"3.8"',
"async-timeout>=4.0.2",
Expand Down