Skip to content
Open
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