summaryrefslogtreecommitdiff
path: root/redis/connection.py
diff options
context:
space:
mode:
authorCody-G <codyjgreer@gmail.com>2020-02-08 13:19:17 -0800
committerAndy McCurdy <andy@andymccurdy.com>2020-02-24 15:38:05 -0800
commit3310fe415a4c3a6ec9a9fcaf9a9fa24808219429 (patch)
tree9f02ba16ce16ed29a86a0482a6b237c38ed6752d /redis/connection.py
parent03260002ded57223c5d05b2c99a0a710ee5d34f3 (diff)
downloadredis-py-3310fe415a4c3a6ec9a9fcaf9a9fa24808219429.tar.gz
Support memoryview encoding/decoding as a no-op
This allows memoryview instances to be passed to Redis command args that expect strings or bytes. The memoryview instance is sent directly to the socket such that there are zero copies made of the underlying data during command packing. Fixes #1265 Fixes #1285
Diffstat (limited to 'redis/connection.py')
-rwxr-xr-xredis/connection.py25
1 files changed, 15 insertions, 10 deletions
diff --git a/redis/connection.py b/redis/connection.py
index f54aace..c61517e 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -94,7 +94,7 @@ SENTINEL = object()
class Encoder(object):
- "Encode strings to bytes and decode bytes to strings"
+ "Encode strings to bytes-like and decode bytes-like to strings"
def __init__(self, encoding, encoding_errors, decode_responses):
self.encoding = encoding
@@ -102,8 +102,8 @@ class Encoder(object):
self.decode_responses = decode_responses
def encode(self, value):
- "Return a bytestring representation of the value"
- if isinstance(value, bytes):
+ "Return a bytestring or bytes-like representation of the value"
+ if isinstance(value, (bytes, memoryview)):
return value
elif isinstance(value, bool):
# special case bool since it is a subclass of int
@@ -124,9 +124,12 @@ class Encoder(object):
return value
def decode(self, value, force=False):
- "Return a unicode string from the byte representation"
- if (self.decode_responses or force) and isinstance(value, bytes):
- value = value.decode(self.encoding, self.encoding_errors)
+ "Return a unicode string from the bytes-like representation"
+ if self.decode_responses or force:
+ if isinstance(value, memoryview):
+ value = value.tobytes()
+ if isinstance(value, bytes):
+ value = value.decode(self.encoding, self.encoding_errors)
return value
@@ -767,9 +770,10 @@ class Connection(object):
buffer_cutoff = self._buffer_cutoff
for arg in imap(self.encoder.encode, args):
# to avoid large string mallocs, chunk the command into the
- # output list if we're sending large values
+ # output list if we're sending large values or memoryviews
arg_length = len(arg)
- if len(buff) > buffer_cutoff or arg_length > buffer_cutoff:
+ if (len(buff) > buffer_cutoff or arg_length > buffer_cutoff
+ or isinstance(arg, memoryview)):
buff = SYM_EMPTY.join(
(buff, SYM_DOLLAR, str(arg_length).encode(), SYM_CRLF))
output.append(buff)
@@ -792,12 +796,13 @@ class Connection(object):
for cmd in commands:
for chunk in self.pack_command(*cmd):
chunklen = len(chunk)
- if buffer_length > buffer_cutoff or chunklen > buffer_cutoff:
+ if (buffer_length > buffer_cutoff or chunklen > buffer_cutoff
+ or isinstance(chunk, memoryview)):
output.append(SYM_EMPTY.join(pieces))
buffer_length = 0
pieces = []
- if chunklen > self._buffer_cutoff:
+ if chunklen > buffer_cutoff or isinstance(chunk, memoryview):
output.append(chunk)
else:
pieces.append(chunk)