Commit 95df76f0 authored by Sean Bleier's avatar Sean Bleier

Adding support for pluggable serializers.

parent f02a00e8
...@@ -13,7 +13,7 @@ development_settings.py ...@@ -13,7 +13,7 @@ development_settings.py
.pydevproject .pydevproject
.settings .settings
dist/* dist/*
dump.rdb *.rdb
dist/* dist/*
MANIFEST MANIFEST
.venv .venv
......
...@@ -5,11 +5,6 @@ from django.utils.importlib import import_module ...@@ -5,11 +5,6 @@ from django.utils.importlib import import_module
from redis_cache.compat import smart_bytes, DEFAULT_TIMEOUT from redis_cache.compat import smart_bytes, DEFAULT_TIMEOUT
try:
import cPickle as pickle
except ImportError:
import pickle
try: try:
import redis import redis
except ImportError: except ImportError:
...@@ -64,6 +59,11 @@ class BaseRedisCache(BaseCache): ...@@ -64,6 +59,11 @@ class BaseRedisCache(BaseCache):
self.connection_pool_class_kwargs = ( self.connection_pool_class_kwargs = (
self.get_connection_pool_class_kwargs() self.get_connection_pool_class_kwargs()
) )
self.serializer_class = self.get_serializer_class()
self.serializer_class_kwargs = self.get_serializer_class_kwargs()
self.serializer = self.serializer_class(
**self.serializer_class_kwargs
)
def __getstate__(self): def __getstate__(self):
return {'params': self.params, 'server': self.server} return {'params': self.params, 'server': self.server}
...@@ -117,6 +117,21 @@ class BaseRedisCache(BaseCache): ...@@ -117,6 +117,21 @@ class BaseRedisCache(BaseCache):
def get_connection_pool_class_kwargs(self): def get_connection_pool_class_kwargs(self):
return self.options.get('CONNECTION_POOL_CLASS_KWARGS', {}) return self.options.get('CONNECTION_POOL_CLASS_KWARGS', {})
def get_serializer_class(self):
serializer_class = self.options.get(
'SERIALIZER_CLASS',
'redis_cache.serializers.PickleSerializer'
)
module_name, class_name = serializer_class.rsplit('.', 1)
module = import_module(module_name)
try:
return getattr(module, class_name)
except AttributeError:
raise ImportError('cannot import name %s' % class_name)
def get_serializer_class_kwargs(self):
return self.options.get('SERIALIZER_CLASS_KWARGS', {})
def get_master_client(self): def get_master_client(self):
""" """
Get the write server:port of the master cache Get the write server:port of the master cache
...@@ -150,14 +165,10 @@ class BaseRedisCache(BaseCache): ...@@ -150,14 +165,10 @@ class BaseRedisCache(BaseCache):
return client return client
def serialize(self, value): def serialize(self, value):
return pickle.dumps(value, self.pickle_version) return self.serializer.serialize(value)
def deserialize(self, value): def deserialize(self, value):
""" return self.serializer.deserialize(value)
Unpickles the given value.
"""
value = smart_bytes(value)
return pickle.loads(value)
def get_value(self, original): def get_value(self, original):
try: try:
......
...@@ -13,7 +13,6 @@ except ImportError: ...@@ -13,7 +13,6 @@ except ImportError:
smart_text = smart_unicode smart_text = smart_unicode
smart_bytes = smart_str smart_bytes = smart_str
if PY3: if PY3:
bytes_type = bytes bytes_type = bytes
from urllib.parse import parse_qs, urlparse from urllib.parse import parse_qs, urlparse
......
try:
import cPickle as pickle
except ImportError:
import pickle
import json
try:
import msgpack
except ImportError:
pass
try:
import yaml
except ImportError:
pass
from redis_cache.compat import smart_bytes, smart_text
class BaseSerializer(object):
def __init__(self, **kwargs):
super(BaseSerializer, self).__init__(**kwargs)
def serialize(self, value):
raise NotImplementedError
def deserialize(self, value):
raise NotImplementedError
class PickleSerializer(object):
def __init__(self, pickle_version=-1):
self.pickle_version = pickle_version
def serialize(self, value):
return pickle.dumps(value, self.pickle_version)
def deserialize(self, value):
return pickle.loads(smart_bytes(value))
class JSONSerializer(BaseSerializer):
def __init__(self, **kwargs):
super(JSONSerializer, self).__init__(**kwargs)
def serialize(self, value):
return smart_bytes(json.dumps(value))
def deserialize(self, value):
return json.loads(smart_text(value))
class MSGPackSerializer(BaseSerializer):
def serialize(self, value):
return msgpack.dumps(value)
def deserialize(self, value):
return msgpack.loads(value, encoding='utf-8')
class YAMLSerializer(BaseSerializer):
def serialize(self, value):
return yaml.dump(value, encoding='utf-8')
def deserialize(self, value):
return yaml.load(value)
...@@ -2,3 +2,5 @@ hiredis==0.2.0 ...@@ -2,3 +2,5 @@ hiredis==0.2.0
django-nose==1.4 django-nose==1.4
nose==1.3.6 nose==1.3.6
unittest2==1.0.1 unittest2==1.0.1
msgpack-python==0.4.6
pyyaml==3.11
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import TestCase
try:
from django.test import override_settings
except ImportError:
from django.test.utils import override_settings
from redis_cache.connection import pool
from tests.testapp.tests.base_tests import SetupMixin
LOCATION = "127.0.0.1:6381"
# functions/classes for complex data type tests
def f():
return 42
class C:
def m(n):
return 24
class BaseSerializerTestCase(SetupMixin, TestCase):
converts_tuple_to_list = False
serializes_objects = True
def test_string(self):
self.cache.set('a', 'a')
self.assertEqual(self.cache.get('a'), 'a')
def test_unicode(self):
self.cache.set('Iñtërnâtiônàlizætiøn', 'Iñtërnâtiônàlizætiøn2')
self.assertEqual(
self.cache.get('Iñtërnâtiônàlizætiøn'),
'Iñtërnâtiônàlizætiøn2'
)
def test_number(self):
self.cache.set('a', 10)
self.assertEqual(self.cache.get('a'), 10)
def test_dictionary(self):
stuff = {
'string': 'this is a string',
'int': 42,
'list': [1, 2, 3, 4],
'tuple': (1, 2, 3, 4),
'dict': {'A': 1, 'B': 2},
}
if self.serializes_objects:
stuff.update({
'function': f,
'class': C,
})
self.cache.set('a', stuff)
stuff = self.cache.get('a')
_tuple = [1, 2, 3, 4] if self.converts_tuple_to_list else (1, 2, 3, 4)
data = {
'string': 'this is a string',
'int': 42,
'list': [1, 2, 3, 4],
'tuple': _tuple,
'dict': {'A': 1, 'B': 2},
}
if self.serializes_objects:
data.update({
'function': f,
'class': C,
})
self.assertEqual(stuff, data)
@override_settings(CACHES={
'default': {
'BACKEND': 'redis_cache.RedisCache',
'LOCATION': LOCATION,
'OPTIONS': {
'DB': 1,
'PASSWORD': 'yadayada',
'PARSER_CLASS': 'redis.connection.HiredisParser',
'PICKLE_VERSION': -1,
'SERIALIZER_CLASS': 'redis_cache.serializers.JSONSerializer'
},
},
})
class JsonSerializerTestCase(BaseSerializerTestCase):
converts_tuple_to_list = True
serializes_objects = False
@override_settings(CACHES={
'default': {
'BACKEND': 'redis_cache.RedisCache',
'LOCATION': LOCATION,
'OPTIONS': {
'DB': 1,
'PASSWORD': 'yadayada',
'PARSER_CLASS': 'redis.connection.HiredisParser',
'PICKLE_VERSION': -1,
'SERIALIZER_CLASS': 'redis_cache.serializers.MSGPackSerializer'
},
},
})
class MSGPackSerializerTestCase(BaseSerializerTestCase):
converts_tuple_to_list = True
serializes_objects = False
@override_settings(CACHES={
'default': {
'BACKEND': 'redis_cache.RedisCache',
'LOCATION': LOCATION,
'OPTIONS': {
'DB': 1,
'PASSWORD': 'yadayada',
'PARSER_CLASS': 'redis.connection.HiredisParser',
'PICKLE_VERSION': -1,
'SERIALIZER_CLASS': 'redis_cache.serializers.YAMLSerializer'
},
},
})
class YAMLSerializerTestCase(BaseSerializerTestCase):
converts_tuple_to_list = False
serializes_objects = True
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment