Skip to content

Commit

Permalink
Add 'raw' and 'compress' options; replace pickle with JSON (#57)
Browse files Browse the repository at this point in the history
* Added the 'compress' option for disabling compression of things that are already compressed, such as JPEG images.
* Replaced pickling with JSON as the default object serialization mechanism, since JSON has no security issues and is often faster.
* Use a 'binary' flag to be able to read and store raw data.
  • Loading branch information
hathawsh authored and jaysonsantos committed Dec 13, 2016
1 parent 0c70b48 commit 8da517d
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 178 deletions.
13 changes: 13 additions & 0 deletions .gitignore
Expand Up @@ -3,3 +3,16 @@
cover
.tox
*.egg-info
.cache
.eggs
bin
include
lib
lib64
local
man
pip-selfcheck.json
pyvenv.cfg
share
_build
dist
117 changes: 72 additions & 45 deletions bmemcached/client.py
@@ -1,7 +1,3 @@
try:
import cPickle as pickle
except ImportError:
import pickle

import six

Expand All @@ -14,27 +10,37 @@
class Client(object):
"""
This is intended to be a client class which implement standard cache interface that common libs do.
:param servers: A list of servers with ip[:port] or unix socket.
:type servers: list
:param username: If your server requires SASL authentication, provide the username.
:type username: six.string_types
:param password: If your server requires SASL authentication, provide the password.
:type password: six.string_types
:param compression: This memcached client uses zlib compression by default,
but you can change it to any Python module that provides
`compress` and `decompress` functions, such as `bz2`.
:type compression: Python module
:param dumps: Use this to replace the object serialization mechanism.
The default is JSON encoding.
:type dumps: function
:param loads: Use this to replace the object deserialization mechanism.
The default is JSON decoding.
:type dumps: function
:param socket_timeout: The timeout applied to memcached connections.
:type socket_timeout: float
"""
def __init__(self, servers=('127.0.0.1:11211',), username=None,
password=None, compression=None,
socket_timeout=_SOCKET_TIMEOUT,
pickle_protocol=0,
pickler=pickle.Pickler, unpickler=pickle.Unpickler):
"""
:param servers: A list of servers with ip[:port] or unix socket.
:type servers: list
:param username: If your server have auth activated, provide it's username.
:type username: six.string_type
:param password: If your server have auth activated, provide it's password.
:type password: six.string_type
"""
dumps=None,
loads=None):
self.username = username
self.password = password
self.compression = compression
self.socket_timeout = socket_timeout
self.pickle_protocol = pickle_protocol
self.pickler = pickler
self.unpickler = unpickler
self.dumps = dumps
self.loads = loads
self.set_servers(servers)

@property
Expand All @@ -55,14 +61,15 @@ def set_servers(self, servers):
servers = [servers]

assert servers, "No memcached servers supplied"
self._servers = [Protocol(server,
self.username,
self.password,
self.compression,
self.socket_timeout,
self.pickle_protocol,
self.pickler,
self.unpickler) for server in servers]
self._servers = [Protocol(
server=server,
username=self.username,
password=self.password,
compression=self.compression,
socket_timeout=self.socket_timeout,
dumps=self.dumps,
loads=self.loads,
) for server in servers]

def _set_retry_delay(self, value):
for server in self._servers:
Expand All @@ -88,7 +95,7 @@ def get(self, key, get_cas=False):
Get a key from server.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:param get_cas: If true, return (value, cas), where cas is the new CAS value.
:type get_cas: boolean
:return: Returns a key data from server.
Expand All @@ -109,7 +116,7 @@ def gets(self, key):
This method is for API compatibility with other implementations.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:return: Returns (key data, value), or (None, None) if the value is not in cache.
:rtype: object
"""
Expand Down Expand Up @@ -143,97 +150,117 @@ def get_multi(self, keys, get_cas=False):
break
return d

def set(self, key, value, time=0):
def set(self, key, value, time=0, compress_level=-1):
"""
Set a value for a key on server.
:param key: Key's name
:type key: six.string_type
:type key: str
:param value: A value to be stored on server.
:type value: object
:param time: Time in seconds that your key will expire.
:type time: int
:param compress_level: How much to compress.
0 = no compression, 1 = fastest, 9 = slowest but best,
-1 = default compression level.
:type compress_level: int
:return: True in case of success and False in case of failure
:rtype: bool
"""
returns = []
for server in self.servers:
returns.append(server.set(key, value, time))
returns.append(server.set(key, value, time, compress_level=compress_level))

return any(returns)

def cas(self, key, value, cas, time=0):
def cas(self, key, value, cas, time=0, compress_level=-1):
"""
Set a value for a key on server if its CAS value matches cas.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:param value: A value to be stored on server.
:type value: object
:param time: Time in seconds that your key will expire.
:type time: int
:param compress_level: How much to compress.
0 = no compression, 1 = fastest, 9 = slowest but best,
-1 = default compression level.
:type compress_level: int
:return: True in case of success and False in case of failure
:rtype: bool
"""
returns = []
for server in self.servers:
returns.append(server.cas(key, value, cas, time))
returns.append(server.cas(key, value, cas, time, compress_level=compress_level))

return any(returns)

def set_multi(self, mappings, time=0):
def set_multi(self, mappings, time=0, compress_level=-1):
"""
Set multiple keys with it's values on server.
:param mappings: A dict with keys/values
:type mappings: dict
:param time: Time in seconds that your key will expire.
:type time: int
:param compress_level: How much to compress.
0 = no compression, 1 = fastest, 9 = slowest but best,
-1 = default compression level.
:type compress_level: int
:return: True in case of success and False in case of failure
:rtype: bool
"""
returns = []
if mappings:
for server in self.servers:
returns.append(server.set_multi(mappings, time))
returns.append(server.set_multi(mappings, time, compress_level=compress_level))

return all(returns)

def add(self, key, value, time=0):
def add(self, key, value, time=0, compress_level=-1):
"""
Add a key/value to server ony if it does not exist.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:param value: A value to be stored on server.
:type value: object
:param time: Time in seconds that your key will expire.
:type time: int
:param compress_level: How much to compress.
0 = no compression, 1 = fastest, 9 = slowest but best,
-1 = default compression level.
:type compress_level: int
:return: True if key is added False if key already exists
:rtype: bool
"""
returns = []
for server in self.servers:
returns.append(server.add(key, value, time))
returns.append(server.add(key, value, time, compress_level=compress_level))

return any(returns)

def replace(self, key, value, time=0):
def replace(self, key, value, time=0, compress_level=-1):
"""
Replace a key/value to server ony if it does exist.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:param value: A value to be stored on server.
:type value: object
:param time: Time in seconds that your key will expire.
:type time: int
:param compress_level: How much to compress.
0 = no compression, 1 = fastest, 9 = slowest but best,
-1 = default compression level.
:type compress_level: int
:return: True if key is replace False if key does not exists
:rtype: bool
"""
returns = []
for server in self.servers:
returns.append(server.replace(key, value, time))
returns.append(server.replace(key, value, time, compress_level=compress_level))

return any(returns)

Expand All @@ -242,7 +269,7 @@ def delete(self, key, cas=0):
Delete a key/value from server. If key does not exist, it returns True.
:param key: Key's name to be deleted
:type key: six.string_type
:type key: six.string_types
:return: True in case o success and False in case of failure.
:rtype: bool
"""
Expand All @@ -264,7 +291,7 @@ def incr(self, key, value):
Increment a key, if it exists, returns it's actual value, if it don't, return 0.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:param value: Number to be incremented
:type value: int
:return: Actual value of the key on server
Expand All @@ -282,7 +309,7 @@ def decr(self, key, value):
Minimum value of decrement return is 0.
:param key: Key's name
:type key: six.string_type
:type key: six.string_types
:param value: Number to be decremented
:type value: int
:return: Actual value of the key on server
Expand Down Expand Up @@ -314,7 +341,7 @@ def stats(self, key=None):
Return server stats.
:param key: Optional if you want status from a key.
:type key: six.string_type
:type key: six.string_types
:return: A dict with server stats
:rtype: dict
"""
Expand Down

0 comments on commit 8da517d

Please sign in to comment.