summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChayim I. Kirshen <c@kirshen.com>2021-11-21 13:17:31 +0200
committerChayim I. Kirshen <c@kirshen.com>2021-11-21 13:17:31 +0200
commit1448a65e656a270caf4c6d3ba4eee5de04d0c4e1 (patch)
tree3b94bf968ef0d0d8b9820f8e8c390cab752df07a
parentd2b233384458869270352b8c99ca682ae480da5f (diff)
downloadredis-py-ck-binary-responses.tar.gz
Adding support for non-decodable commandsck-binary-responses
Some commands (i.e DUMP) should never have their response decoded, as they return binaries, not encoded blobs fixes #1254
-rwxr-xr-xredis/client.py8
-rw-r--r--redis/commands/core.py5
-rwxr-xr-xredis/connection.py15
-rw-r--r--redis/sentinel.py4
4 files changed, 22 insertions, 10 deletions
diff --git a/redis/client.py b/redis/client.py
index dc6693d..b218c1d 100755
--- a/redis/client.py
+++ b/redis/client.py
@@ -27,6 +27,9 @@ from redis.utils import safe_str, str_if_bytes
SYM_EMPTY = b''
EMPTY_RESPONSE = 'EMPTY_RESPONSE'
+# some responses (ie. dump) are binary, and just meant to never be decoded
+NEVER_DECODE = 'NEVER_DECODE'
+
def timestamp_to_datetime(response):
"Converts a unix timestamp to a Python datetime object"
@@ -1081,7 +1084,10 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands, object):
def parse_response(self, connection, command_name, **options):
"Parses a response from the Redis server"
try:
- response = connection.read_response()
+ if NEVER_DECODE in options:
+ response = connection.read_response(disable_decoding=True)
+ else:
+ response = connection.read_response()
except ResponseError:
if EMPTY_RESPONSE in options:
return options[EMPTY_RESPONSE]
diff --git a/redis/commands/core.py b/redis/commands/core.py
index 516e7d9..09fcc5d 100644
--- a/redis/commands/core.py
+++ b/redis/commands/core.py
@@ -801,7 +801,10 @@ class CoreCommands:
Return a serialized version of the value stored at the specified key.
If key does not exist a nil bulk reply is returned.
"""
- return self.execute_command('DUMP', name)
+ from redis.client import NEVER_DECODE
+ options = {}
+ options[NEVER_DECODE] = []
+ return self.execute_command('DUMP', name, **options)
def exists(self, *names):
"Returns the number of ``names`` that exist"
diff --git a/redis/connection.py b/redis/connection.py
index e01742d..3de87ae 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -314,7 +314,7 @@ class PythonParser(BaseParser):
def can_read(self, timeout):
return self._buffer and self._buffer.can_read(timeout)
- def read_response(self):
+ def read_response(self, disable_decoding=False):
raw = self._buffer.readline()
if not raw:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
@@ -354,8 +354,9 @@ class PythonParser(BaseParser):
length = int(response)
if length == -1:
return None
- response = [self.read_response() for i in range(length)]
- if isinstance(response, bytes):
+ response = [self.read_response(disable_decoding=disable_decoding)
+ for i in range(length)]
+ if isinstance(response, bytes) and disable_decoding is False:
response = self.encoder.decode(response)
return response
@@ -449,7 +450,7 @@ class HiredisParser(BaseParser):
if custom_timeout:
sock.settimeout(self._socket_timeout)
- def read_response(self):
+ def read_response(self, disable_decoding=False):
if not self._reader:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
@@ -742,10 +743,12 @@ class Connection:
self.connect()
return self._parser.can_read(timeout)
- def read_response(self):
+ def read_response(self, disable_decoding=False):
"""Read the response from a previously sent command"""
try:
- response = self._parser.read_response()
+ response = self._parser.read_response(
+ disable_decoding=disable_decoding
+ )
except socket.timeout:
self.disconnect()
raise TimeoutError("Timeout reading from %s:%s" %
diff --git a/redis/sentinel.py b/redis/sentinel.py
index 17dd75b..3efd58f 100644
--- a/redis/sentinel.py
+++ b/redis/sentinel.py
@@ -51,9 +51,9 @@ class SentinelManagedConnection(Connection):
continue
raise SlaveNotFoundError # Never be here
- def read_response(self):
+ def read_response(self, disable_decoding=False):
try:
- return super().read_response()
+ return super().read_response(disable_decoding=disable_decoding)
except ReadOnlyError:
if self.connection_pool.is_master:
# When talking to a master, a ReadOnlyError when likely