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

Add raw_bytes option #126

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/python_memcached.egg-info
.tox
.coverage
.idea
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* Added `raw_bytes` option to avoid decoding bytes to str

* Added testing for Python 3.5 (PR from Tim Graham) #110

* Fixed typos in docstrings (PR from Romuald Brunet, reviewed by Tim
Expand Down
34 changes: 22 additions & 12 deletions memcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ def __init__(self, servers, debug=0, pickleProtocol=0,
pload=None, pid=None,
server_max_key_length=None, server_max_value_length=None,
dead_retry=_DEAD_RETRY, socket_timeout=_SOCKET_TIMEOUT,
cache_cas=False, flush_on_reconnect=0, check_keys=True):
cache_cas=False, flush_on_reconnect=0, check_keys=True,
raw_bytes=False):
"""Create a new Client object with the given list of servers.

@param servers: C{servers} is passed to L{set_servers}.
Expand Down Expand Up @@ -205,6 +206,8 @@ def __init__(self, servers, debug=0, pickleProtocol=0,
@param check_keys: (default True) If True, the key is checked
to ensure it is the correct length and composed of the right
characters.
@param raw_bytes: (default False) if True, we return raw
bytes instead of trying to decode to Unicode string.
"""
super(Client, self).__init__()
self.debug = debug
Expand All @@ -216,6 +219,7 @@ def __init__(self, servers, debug=0, pickleProtocol=0,
self.cache_cas = cache_cas
self.reset_cas()
self.do_check_key = check_keys
self.raw_bytes = raw_bytes

# Allow users to modify pickling/unpickling behavior
self.pickleProtocol = pickleProtocol
Expand All @@ -232,7 +236,7 @@ def __init__(self, servers, debug=0, pickleProtocol=0,
if self.server_max_value_length is None:
self.server_max_value_length = SERVER_MAX_VALUE_LENGTH

# figure out the pickler style
# figure out the pickler style
file = BytesIO()
try:
pickler = self.pickler(file, protocol=self.pickleProtocol)
Expand Down Expand Up @@ -811,7 +815,7 @@ def _map_and_prefix_keys(self, key_iterable, key_prefix):
bytes_orig_key = key
server, key = self._get_server(key_prefix + key)

# alert when passed in key is None
# alert when passed in key is None
if orig_key is None:
self.check_key(orig_key, key_extra_len=key_extra_len)

Expand Down Expand Up @@ -931,7 +935,7 @@ def set_multi(self, mapping, time=0, key_prefix='', min_compress_len=0,
for server in dead_servers:
del server_keys[server]

# short-circuit if there are no servers, just return all keys
# short-circuit if there are no servers, just return all keys
if not server_keys:
return list(mapping.keys())

Expand Down Expand Up @@ -997,9 +1001,9 @@ def _val_to_store_info(self, val, min_compress_len):
flags |= Client._FLAG_COMPRESSED
val = comp_val

# silently do not store if value length exceeds maximum
if (self.server_max_value_length != 0 and
len(val) > self.server_max_value_length):
# silently do not store if value length exceeds maximum
if ((self.server_max_value_length != 0
and len(val) > self.server_max_value_length)):
return 0

return (flags, len(val), val)
Expand Down Expand Up @@ -1254,7 +1258,7 @@ def _recv_value(self, server, flags, rlen):

if flags == 0:
# Bare string
if six.PY3:
if not self.raw_bytes and six.PY3:
val = buf.decode('utf8')
else:
val = buf
Expand Down Expand Up @@ -1301,14 +1305,14 @@ def check_key(self, key, key_extra_len=0):
if key_extra_len is 0:
raise Client.MemcachedKeyNoneError("Key is empty")

# key is empty but there is some other component to key
# key is empty but there is some other component to key
return

if not isinstance(key, six.binary_type):
raise Client.MemcachedKeyTypeError("Key must be a binary string")

if (self.server_max_key_length != 0 and
len(key) + key_extra_len > self.server_max_key_length):
if ((self.server_max_key_length != 0
and len(key) + key_extra_len > self.server_max_key_length)):
raise Client.MemcachedKeyLengthError(
"Key length is > %s" % self.server_max_key_length
)
Expand All @@ -1330,7 +1334,7 @@ def __init__(self, host, debug=0, dead_retry=_DEAD_RETRY,
else:
self.weight = 1

# parse the connection string
# parse the connection string
m = re.match(r'^(?P<proto>unix):(?P<path>.*)$', host)
if not m:
m = re.match(r'^(?P<proto>inet6):'
Expand Down Expand Up @@ -1397,13 +1401,19 @@ def _get_socket(self):
try:
s.connect(self.address)
except socket.timeout as msg:
s.close()
self.mark_dead("connect: %s" % msg)
return None
except socket.error as msg:
s.close()
if isinstance(msg, tuple):
msg = msg[1]
self.mark_dead("connect: %s" % msg)
return None
except Exception as e:
# print("Other error: %s" % (e,))
self.debuglog("Other error: %s" % (e,))
raise
self.socket = s
self.buffer = b''
if self.flush_on_next_connect:
Expand Down