From 4c9285cdd41863250faeb03e0b3f93e1568c0858 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 19 Sep 2022 12:19:55 +0300 Subject: [PATCH 1/3] Only set HIREDIS_AVAILABLE if Hiredis is not 0.x --- README.md | 3 ++- redis/utils.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c7b839d032..8b7333e157 100644 --- a/README.md +++ b/README.md @@ -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] diff --git a/redis/utils.py b/redis/utils.py index 0c34e1eef8..25d2491dfe 100644 --- a/redis/utils.py +++ b/redis/utils.py @@ -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 From fb05a391d86e84e7fe0137139c0226ecbf0ab338 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 19 Sep 2022 12:21:48 +0300 Subject: [PATCH 2/3] Remove compatibility code for old Hiredis versions --- CHANGES | 1 + redis/connection.py | 54 +++++++++------------------------------------ 2 files changed, 11 insertions(+), 44 deletions(-) diff --git a/CHANGES b/CHANGES index 47600cf0d0..75fa83cbe2 100644 --- a/CHANGES +++ b/CHANGES @@ -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) diff --git a/redis/connection.py b/redis/connection.py index 3281b14d80..491df8e027 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -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, @@ -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" @@ -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: @@ -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 @@ -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 @@ -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 From 9dcbeaee5b71c93fce925651ac37b7f1bb894b9d Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 19 Sep 2022 12:23:44 +0300 Subject: [PATCH 3/3] Move packaging dependency to dev only --- dev_requirements.txt | 1 + requirements.txt | 1 - setup.py | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 31ae26ebea..66be6d1d64 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -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 diff --git a/requirements.txt b/requirements.txt index c40eca70e0..b764668e7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ async-timeout>=4.0.2 deprecated>=1.2.3 -packaging>=20.4 typing-extensions; python_version<"3.8" diff --git a/setup.py b/setup.py index fb6d32a31d..e08251f1ac 100644 --- a/setup.py +++ b/setup.py @@ -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",