Skip to content

Commit

Permalink
dill serializer support added
Browse files Browse the repository at this point in the history
  • Loading branch information
George Alexiou committed Feb 27, 2021
1 parent 9c8b07f commit 8e1a182
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ format:
black -l 100 tests/ aiocache/

install-dev:
pip install -e .[redis,memcached,msgpack,dev]
pip install -e .[redis,memcached,msgpack,dill,dev]

pylint:
pylint --disable=C0111 aiocache
Expand Down
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Installing
- ``pip install aiocache[memcached]``
- ``pip install aiocache[redis,memcached]``
- ``pip install aiocache[msgpack]``
- ``pip install aiocache[dill]``


Usage
Expand Down Expand Up @@ -168,7 +169,7 @@ How does it work
Aiocache provides 3 main entities:

- **backends**: Allow you specify which backend you want to use for your cache. Currently supporting: SimpleMemoryCache, RedisCache using aioredis_ and MemCache using aiomcache_.
- **serializers**: Serialize and deserialize the data between your code and the backends. This allows you to save any Python object into your cache. Currently supporting: StringSerializer, PickleSerializer, JsonSerializer, and MsgPackSerializer. But you can also build custom ones.
- **serializers**: Serialize and deserialize the data between your code and the backends. This allows you to save any Python object into your cache. Currently supporting: StringSerializer, PickleSerializer, JsonSerializer, MsgPackSerializer and DillSerializer. But you can also build custom ones.
- **plugins**: Implement a hooks system that allows to execute extra behavior before and after of each command.

If you are missing an implementation of backend, serializer or plugin you think it could be interesting for the package, do not hesitate to open a new issue.
Expand Down
9 changes: 9 additions & 0 deletions aiocache/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@

del msgpack

try:
import dill
except ImportError:
logger.debug("dill not installed, DillSerializer unavailable")
else:
from .serializers import DillSerializer

del dill

__all__ = [
"BaseSerializer",
Expand All @@ -28,4 +36,5 @@
"PickleSerializer",
"JsonSerializer",
"MsgPackSerializer",
"DillSerializer",
]
38 changes: 38 additions & 0 deletions aiocache/serializers/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
msgpack = None
logger.debug("msgpack not installed, MsgPackSerializer unavailable")

try:
import dill
except ImportError:
dill = None
logger.debug("dill not installed, DillSerializer unavailable")


_NOT_SET = object()

Expand Down Expand Up @@ -193,3 +199,35 @@ def loads(self, value):
if value is None:
return None
return msgpack.loads(value, raw=raw, use_list=self.use_list)


class DillSerializer(BaseSerializer):
"""
Transform data to bytes using dill.dumps and dill.loads to retrieve it back.
"""

DEFAULT_ENCODING = None

def __init__(self, *args, protocol=dill.DEFAULT_PROTOCOL, **kwargs):
super().__init__(*args, **kwargs)
self.protocol = protocol

def dumps(self, value):
"""
Serialize the received value using ``dill.dumps``.
:param value: obj
:returns: bytes
"""
return dill.dumps(value, protocol=self.protocol)

def loads(self, value):
"""
Deserialize value using ``dill.loads``.
:param value: bytes
:returns: obj
"""
if value is None:
return None
return dill.loads(value)
1 change: 1 addition & 0 deletions docs/readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ python:
- redis
- memcached
- msgpack
- dill
8 changes: 8 additions & 0 deletions docs/serializers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ MsgPackSerializer
.. autoclass:: aiocache.serializers.MsgPackSerializer
:members:

.. _dillserializer:

DillSerializer
----------------

.. autoclass:: aiocache.serializers.DillSerializer
:members:

In case the current serializers are not covering your needs, you can always define your custom serializer as shown in ``examples/serializer_class.py``:

.. literalinclude:: ../examples/serializer_class.py
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
'redis:python_version>="3.7" and python_version<"3.8"': ['aioredis>=1.0.0'],
'memcached': ['aiomcache>=0.5.2'],
'msgpack': ['msgpack>=0.5.5'],
'dill': ['dill>=0.3.3'],
'dev': [
'asynctest>=0.11.0',
'black;python_version>="3.6"',
Expand Down
40 changes: 40 additions & 0 deletions tests/ut/test_serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
import pickle
import dill

from collections import namedtuple
from unittest import mock
Expand All @@ -11,6 +12,7 @@
PickleSerializer,
JsonSerializer,
MsgPackSerializer,
DillSerializer,
)


Expand Down Expand Up @@ -189,3 +191,41 @@ def test_dumps_and_loads_dict(self):
"a": [1, 2, ["1", 2]],
"b": {"b": 1, "c": [1, 2]},
}


class TestDillSerializer:
@pytest.fixture
def serializer(self):
yield DillSerializer(protocol=4)

def test_init(self, serializer):
assert isinstance(serializer, BaseSerializer)
assert serializer.DEFAULT_ENCODING is None
assert serializer.encoding is None
assert serializer.protocol == 4

def test_init_sets_default_protocol(self):
serializer = DillSerializer()
assert serializer.protocol == dill.DEFAULT_PROTOCOL

@pytest.mark.parametrize("obj", TYPES)
def test_set_types(self, obj, serializer):
assert serializer.loads(serializer.dumps(obj)) == obj

def test_dumps(self, serializer):
assert (
serializer.dumps("hi") == b"\x80\x04\x95\x06\x00\x00\x00\x00\x00\x00\x00\x8c\x02hi\x94."
)

def test_dumps_with_none(self, serializer):
assert isinstance(serializer.dumps(None), bytes)

def test_loads(self, serializer):
assert serializer.loads(b"\x80\x03X\x02\x00\x00\x00hiq\x00.") == "hi"

def test_loads_with_none(self, serializer):
assert serializer.loads(None) is None

def test_dumps_and_loads(self, serializer):
obj = Dummy(1, 2)
assert serializer.loads(serializer.dumps(obj)) == obj

0 comments on commit 8e1a182

Please sign in to comment.