Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Return pickle as default serializer #73

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

Merged
merged 2 commits into from
Jan 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ language: python
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
install: pip install -r requirements_test.txt && python setup.py develop
script: ./.travis-runs-tests.sh
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
exclude *.pyc *.pyo
exclude **/__pycache__/**
recursive-include docs *
recursive-include tests *
prune docs/_build
29 changes: 17 additions & 12 deletions bmemcached/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@

import six
try:
import cPickle as pickle
except ImportError:
import pickle as pickle

from bmemcached.protocol import Protocol

Expand All @@ -21,26 +25,26 @@ class Client(object):
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 pickler: Use this to replace the object serialization mechanism.
:type pickler: function
:param unpickler: Use this to replace the object deserialization mechanism.
:type unpickler: 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,
dumps=None,
loads=None):
pickle_protocol=0,
pickler=pickle.Pickler,
unpickler=pickle.Unpickler):
self.username = username
self.password = password
self.compression = compression
self.socket_timeout = socket_timeout
self.dumps = dumps
self.loads = loads
self.pickle_protocol = pickle_protocol
self.pickler = pickler
self.unpickler = unpickler
self.set_servers(servers)

@property
Expand All @@ -67,8 +71,9 @@ def set_servers(self, servers):
password=self.password,
compression=self.compression,
socket_timeout=self.socket_timeout,
dumps=self.dumps,
loads=self.loads,
pickle_protocol=self.pickle_protocol,
pickler=self.pickler,
unpickler=self.unpickler,
) for server in servers]

def _set_retry_delay(self, value):
Expand Down
30 changes: 12 additions & 18 deletions bmemcached/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from urllib.parse import splitport

import zlib
from io import BytesIO
import six
from six import binary_type, text_type
import json

from bmemcached.compat import long
from bmemcached.exceptions import AuthenticationNotSupported, InvalidCredentials, MemcachedException
Expand Down Expand Up @@ -79,7 +79,7 @@ class Protocol(threading.local):
COMPRESSION_THRESHOLD = 128

def __init__(self, server, username=None, password=None, compression=None, socket_timeout=None,
dumps=None, loads=None):
pickle_protocol=None, pickler=None, unpickler=None):
super(Protocol, self).__init__()
self.server = server
self._username = username
Expand All @@ -89,8 +89,9 @@ def __init__(self, server, username=None, password=None, compression=None, socke
self.connection = None
self.authenticated = False
self.socket_timeout = socket_timeout
self.dumps = dumps
self.loads = loads
self.pickle_protocol = pickle_protocol
self.pickler = pickler
self.unpickler = unpickler

self.reconnects_deferred_until = None

Expand Down Expand Up @@ -323,10 +324,10 @@ def serialize(self, value, compress_level=-1):
value = str(value)
else:
flags |= self.FLAGS['object']
dumps = self.dumps
if dumps is None:
dumps = self.json_dumps
value = dumps(value)
buf = BytesIO()
pickler = self.pickler(buf, self.pickle_protocol)
pickler.dump(value)
value = buf.getvalue()

if compress_level != 0 and len(value) > self.COMPRESSION_THRESHOLD:
if compress_level is not None and compress_level > 0:
Expand Down Expand Up @@ -366,10 +367,9 @@ def deserialize(self, value, flags):
elif flags & FLAGS['long']:
return long(value)
elif flags & FLAGS['object']:
loads = self.loads
if loads is None:
loads = self.json_loads
return loads(value)
buf = BytesIO(value)
unpickler = self.unpickler(buf)
return unpickler.load()

if six.PY3:
return value.decode('utf8')
Expand All @@ -383,12 +383,6 @@ def deserialize(self, value, flags):
else:
return value

def json_dumps(self, value):
return json.dumps(value).encode('utf8')

def json_loads(self, value):
return json.loads(value.decode('utf8'))

def get(self, key):
"""
Get a key and its CAS value from server. If the value isn't cached, return
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from setuptools import setup
setup(
name='python-binary-memcached',
version='0.25.0',
version='0.26.0',
author='Jayson Reis',
author_email='[email protected]',
description='A pure python module to access memcached via its binary protocol with SASL auth support',
Expand Down
6 changes: 5 additions & 1 deletion test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import os
import subprocess
import time

import pytest
import time


os.environ.setdefault('MEMCACHED_HOST', '127.0.0.1')


@pytest.yield_fixture(scope='session', autouse=True)
Expand Down
15 changes: 9 additions & 6 deletions test/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import six
import os
import unittest

import six

import bmemcached
from bmemcached.exceptions import AuthenticationNotSupported, InvalidCredentials, MemcachedException

Expand All @@ -17,7 +20,7 @@ def testServerDoesntNeedAuth(self, mocked_response):
authenticating, it isn't needed.
"""
mocked_response.return_value = (0, 0, 0, 0, 0, 0x81, 0, 0, 0, 0)
server = bmemcached.client.Protocol('127.0.0.1')
server = bmemcached.client.Protocol(os.environ['MEMCACHED_HOST'])
# can pass anything and it'll work
self.assertTrue(server.authenticate('user', 'badpassword'))

Expand All @@ -27,7 +30,7 @@ def testNotUsingPlainAuth(self, mocked_response):
Raise AuthenticationNotSupported unless we're using PLAIN auth.
"""
mocked_response.return_value = (0, 0, 0, 0, 0, 0, 0, 0, 0, [])
server = bmemcached.client.Protocol('127.0.0.1')
server = bmemcached.client.Protocol(os.environ['MEMCACHED_HOST'])
self.assertRaises(AuthenticationNotSupported,
server.authenticate, 'user', 'password')

Expand All @@ -37,7 +40,7 @@ def testAuthNotSuccessful(self, mocked_response):
Raise MemcachedException for anything unsuccessful.
"""
mocked_response.return_value = (0, 0, 0, 0, 0, 0x01, 0, 0, 0, [b'PLAIN'])
server = bmemcached.client.Protocol('127.0.0.1')
server = bmemcached.client.Protocol(os.environ['MEMCACHED_HOST'])
self.assertRaises(MemcachedException,
server.authenticate, 'user', 'password')

Expand All @@ -47,7 +50,7 @@ def testAuthSuccessful(self, mocked_response):
Valid logins return True.
"""
mocked_response.return_value = (0, 0, 0, 0, 0, 0, 0, 0, 0, [b'PLAIN'])
server = bmemcached.client.Protocol('127.0.0.1')
server = bmemcached.client.Protocol(os.environ['MEMCACHED_HOST'])
self.assertTrue(server.authenticate('user', 'password'))

@mock.patch.object(bmemcached.client.Protocol, '_get_response')
Expand All @@ -56,6 +59,6 @@ def testAuthUnsuccessful(self, mocked_response):
Invalid logins raise InvalidCredentials
"""
mocked_response.return_value = (0, 0, 0, 0, 0, 0x08, 0, 0, 0, [b'PLAIN'])
server = bmemcached.client.Protocol('127.0.0.1')
server = bmemcached.client.Protocol(os.environ['MEMCACHED_HOST'])
self.assertRaises(InvalidCredentials, server.authenticate,
'user', 'password2')
11 changes: 7 additions & 4 deletions test/test_compression.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import unittest
import bz2
import bmemcached
import os
import unittest

import six

import bmemcached

if six.PY3:
from unittest import mock
else:
Expand All @@ -11,7 +14,7 @@

class MemcachedTests(unittest.TestCase):
def setUp(self):
self.server = '127.0.0.1:11211'
self.server = '{}:11211'.format(os.environ['MEMCACHED_HOST'])
self.client = bmemcached.Client(self.server, 'user', 'password')
self.bzclient = bmemcached.Client(self.server, 'user', 'password',
compression=bz2)
Expand All @@ -35,7 +38,7 @@ def testCompressionMissmatch(self):
self.client.set('test_key', self.data)
self.bzclient.set('test_key2', self.data)
self.assertEqual(self.client.get('test_key'),
self.bzclient.get('test_key2'))
self.bzclient.get('test_key2'))
self.assertRaises(IOError, self.bzclient.get, 'test_key')

def testCompressionEnabled(self):
Expand Down
16 changes: 12 additions & 4 deletions test/test_error_handling.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import multiprocessing, select, socket, threading, time, unittest
import multiprocessing
import os
import select
import socket
import time
import unittest

import bmemcached
from bmemcached.protocol import Protocol

Expand All @@ -14,7 +20,7 @@ def run(self):
listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_sock.setblocking(False)
listen_sock.bind(('127.0.0.1', self._listen_port or 0))
listen_sock.bind((os.environ['MEMCACHED_HOST'], self._listen_port or 0))
listen_sock.listen(1)

# Tell our caller the (host, port) that we're listening on.
Expand Down Expand Up @@ -85,6 +91,7 @@ def run(self):
bytes_written = client_sock.send(data_for_client)
data_for_client = data_for_client[bytes_written:]


class MemcachedTests(unittest.TestCase):
def setUp(self):
self._proxy_port = None
Expand All @@ -106,7 +113,7 @@ def setUp(self):
self.client.delete('test_key2')

def _server_host(self):
return '127.0.0.1:11211'
return '{}:11211'.format(os.environ['MEMCACHED_HOST'])

def _start_proxy(self):
# Start the proxy. If this isn't the first time we've started the proxy,
Expand Down Expand Up @@ -250,10 +257,11 @@ def testStats(self):
stats = self.client.stats()[self.server]
self.assertEqual(stats, {})


class SocketMemcachedTests(MemcachedTests):
"""
Same tests as above, just make sure it works with sockets.
"""

def _server_host(self):
return '/tmp/memcached.sock'

17 changes: 10 additions & 7 deletions test/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import six
import os
import unittest

import six

import bmemcached
from bmemcached.exceptions import MemcachedException

if six.PY3:
from unittest import mock
else:
Expand All @@ -14,7 +18,7 @@ def testGet(self):
Raise MemcachedException if request wasn't successful and
wasn't a 'key not found' error.
"""
client = bmemcached.Client('127.0.0.1:11211', 'user', 'password')
client = bmemcached.Client('{}:11211'.format(os.environ['MEMCACHED_HOST']), 'user', 'password')
with mock.patch.object(bmemcached.client.Protocol, '_get_response') as mocked_response:
mocked_response.return_value = (0, 0, 0, 0, 0, 0x81, 0, 0, 0, 0)
self.assertRaises(MemcachedException, client.get, 'foo')
Expand All @@ -24,7 +28,7 @@ def testSet(self):
Raise MemcachedException if request wasn't successful and
wasn't a 'key not found' or 'key exists' error.
"""
client = bmemcached.Client('127.0.0.1:11211', 'user', 'password')
client = bmemcached.Client('{}:11211'.format(os.environ['MEMCACHED_HOST']), 'user', 'password')
with mock.patch.object(bmemcached.client.Protocol, '_get_response') as mocked_response:
mocked_response.return_value = (0, 0, 0, 0, 0, 0x81, 0, 0, 0, 0)
self.assertRaises(MemcachedException, client.set, 'foo', 'bar', 300)
Expand All @@ -34,7 +38,7 @@ def testIncrDecr(self):
Incr/Decr raise MemcachedException unless the request wasn't
successful.
"""
client = bmemcached.Client('127.0.0.1:11211', 'user', 'password')
client = bmemcached.Client('{}:11211'.format(os.environ['MEMCACHED_HOST']), 'user', 'password')
client.set('foo', 1)
with mock.patch.object(bmemcached.client.Protocol, '_get_response') as mocked_response:
mocked_response.return_value = (0, 0, 0, 0, 0, 0x81, 0, 0, 0, 2)
Expand All @@ -45,7 +49,7 @@ def testDelete(self):
"""
Raise MemcachedException if the delete request isn't successful.
"""
client = bmemcached.Client('127.0.0.1:11211', 'user', 'password')
client = bmemcached.Client('{}:11211'.format(os.environ['MEMCACHED_HOST']), 'user', 'password')
client.flush_all()
with mock.patch.object(bmemcached.client.Protocol, '_get_response') as mocked_response:
mocked_response.return_value = (0, 0, 0, 0, 0, 0x81, 0, 0, 0, 0)
Expand All @@ -55,8 +59,7 @@ def testFlushAll(self):
"""
Raise MemcachedException if the flush wasn't successful.
"""
client = bmemcached.Client('127.0.0.1:11211', 'user', 'password')
client = bmemcached.Client('{}:11211'.format(os.environ['MEMCACHED_HOST']), 'user', 'password')
with mock.patch.object(bmemcached.client.Protocol, '_get_response') as mocked_response:
mocked_response.return_value = (0, 0, 0, 0, 0, 0x81, 0, 0, 0, 0)
self.assertRaises(MemcachedException, client.flush_all)

Loading