Skip to content

Commit 1de445d

Browse files
authored
Memory now uses new NullSerializer (#273)
Memory is a special case and doesn't need a serializer because anything can be stored in memory. Created a new NullSerializer that does nothing which is the default that SimpleMemoryCache will use now.
1 parent 1701228 commit 1de445d

18 files changed

+113
-29
lines changed

aiocache/backends/memcached.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import aiomcache
33

44
from aiocache.base import BaseCache
5+
from aiocache.serializers import JsonSerializer
56

67

78
class MemcachedBackend:
@@ -114,7 +115,7 @@ async def _close(self, *args, _conn=None, **kwargs):
114115
class MemcachedCache(MemcachedBackend, BaseCache):
115116
"""
116117
Memcached cache implementation with the following components as defaults:
117-
- serializer: :class:`aiocache.serializers.StringSerializer`
118+
- serializer: :class:`aiocache.serializers.JsonSerializer`
118119
- plugins: []
119120
120121
Config options are:
@@ -129,8 +130,9 @@ class MemcachedCache(MemcachedBackend, BaseCache):
129130
:param port: int with the port to connect to. Default is 11211.
130131
:param pool_size: int size for memcached connections pool. Default is 2.
131132
"""
132-
def __init__(self, **kwargs):
133+
def __init__(self, serializer=None, **kwargs):
133134
super().__init__(**kwargs)
135+
self.serializer = serializer or JsonSerializer()
134136

135137
def _build_key(self, key, namespace=None):
136138
ns_key = super()._build_key(key, namespace=namespace).replace(' ', '_')

aiocache/backends/memory.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22

33
from aiocache.base import BaseCache
4+
from aiocache.serializers import NullSerializer
45

56

67
class SimpleMemoryBackend:
@@ -101,7 +102,7 @@ def __delete(cls, key):
101102
class SimpleMemoryCache(SimpleMemoryBackend, BaseCache):
102103
"""
103104
Memory cache implementation with the following components as defaults:
104-
- serializer: :class:`aiocache.serializers.StringSerializer`
105+
- serializer: :class:`aiocache.serializers.JsonSerializer`
105106
- plugins: None
106107
107108
Config options are:
@@ -113,5 +114,6 @@ class SimpleMemoryCache(SimpleMemoryBackend, BaseCache):
113114
:param timeout: int or float in seconds specifying maximum timeout for the operations to last.
114115
By default its 5.
115116
"""
116-
def __init__(self, **kwargs):
117+
def __init__(self, serializer=None, **kwargs):
117118
super().__init__(**kwargs)
119+
self.serializer = serializer or NullSerializer()

aiocache/backends/redis.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import aioredis
66

77
from aiocache.base import BaseCache
8+
from aiocache.serializers import JsonSerializer
89

910

1011
def conn(func):
@@ -164,7 +165,7 @@ async def _connect(self):
164165
class RedisCache(RedisBackend, BaseCache):
165166
"""
166167
Redis cache implementation with the following components as defaults:
167-
- serializer: :class:`aiocache.serializers.StringSerializer`
168+
- serializer: :class:`aiocache.serializers.JsonSerializer`
168169
- plugins: []
169170
170171
Config options are:
@@ -182,8 +183,9 @@ class RedisCache(RedisBackend, BaseCache):
182183
:param pool_min_size: int minimum pool size for the redis connections pool. Default is 1
183184
:param pool_max_size: int maximum pool size for the redis connections pool. Default is 10
184185
"""
185-
def __init__(self, **kwargs):
186+
def __init__(self, serializer=None, **kwargs):
186187
super().__init__(**kwargs)
188+
self.serializer = serializer or JsonSerializer()
187189

188190
def _build_key(self, key, namespace=None):
189191
if namespace is not None:

aiocache/decorators.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
from aiocache.log import logger
55
from aiocache import SimpleMemoryCache, caches
6-
from aiocache.serializers import JsonSerializer
76

87

98
class cached:
@@ -26,7 +25,7 @@ class cached:
2625
:param cache: cache class to use when calling the ``set``/``get`` operations.
2726
Default is ``aiocache.SimpleMemoryCache``.
2827
:param serializer: serializer instance to use when calling the ``dumps``/``loads``.
29-
Default is JsonSerializer.
28+
If its None, default one from the cache backend is used.
3029
:param plugins: list plugins to use when calling the cmd hooks
3130
Default is pulled from the cache class being used.
3231
:param alias: str specifying the alias to load the config from. If alias is passed, other config
@@ -38,7 +37,7 @@ class cached:
3837

3938
def __init__(
4039
self, ttl=None, key=None, key_from_attr=None, key_builder=None, cache=SimpleMemoryCache,
41-
serializer=JsonSerializer, plugins=None, alias=None, noself=False, **kwargs):
40+
serializer=None, plugins=None, alias=None, noself=False, **kwargs):
4241
self.ttl = ttl
4342
self.key = key
4443
if key_from_attr is not None:
@@ -202,7 +201,7 @@ class multi_cached:
202201
:param cache: cache class to use when calling the ``multi_set``/``multi_get`` operations.
203202
Default is ``aiocache.SimpleMemoryCache``.
204203
:param serializer: serializer instance to use when calling the ``dumps``/``loads``.
205-
Default is JsonSerializer.
204+
If its None, default one from the cache backend is used.
206205
:param plugins: plugins to use when calling the cmd hooks
207206
Default is pulled from the cache class being used.
208207
:param alias: str specifying the alias to load the config from. If alias is passed, other config
@@ -211,7 +210,7 @@ class multi_cached:
211210

212211
def __init__(
213212
self, keys_from_attr, key_builder=None, ttl=0, cache=SimpleMemoryCache,
214-
serializer=JsonSerializer, plugins=None, alias=None, **kwargs):
213+
serializer=None, plugins=None, alias=None, **kwargs):
215214
self.keys_from_attr = keys_from_attr
216215
self.key_builder = key_builder or (lambda key, *args, **kwargs: key)
217216
self.ttl = ttl

aiocache/serializers.py

+32
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,38 @@
1010
import pickle
1111

1212

13+
class NullSerializer:
14+
"""
15+
This serializer does nothing. Its only recommended to be used by
16+
:class:`aiocache.SimpleMemoryCache` because for other backends it will
17+
produce incompatible data unless you work only with str types.
18+
19+
DISCLAIMER: Be careful with mutable types and memory storage. The following
20+
behavior is considered normal (same as ``functools.lru_cache``)::
21+
22+
cache = SimpleMemoryCache()
23+
my_list = [1]
24+
await cache.set("key", my_list)
25+
my_list.append(2)
26+
await cache.get("key") # Will return [1, 2]
27+
"""
28+
encoding = 'utf-8'
29+
30+
@classmethod
31+
def dumps(cls, value):
32+
"""
33+
Returns the same value
34+
"""
35+
return value
36+
37+
@classmethod
38+
def loads(cls, value):
39+
"""
40+
Returns the same value
41+
"""
42+
return value
43+
44+
1345
class StringSerializer:
1446
"""
1547
Converts all input values to str. All return values are also str. Be

examples/serializer_function.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async def serializer_function():
3838

3939
assert obj.x == 1
4040
assert obj.y == 2
41-
assert json.loads((await cache.get("key"))) == json.loads(('{"y": 2.0, "x": 1.0}'))
41+
assert await cache.get("key") == json.loads(('{"y": 2.0, "x": 1.0}'))
4242
assert json.loads(await cache.raw("get", "main:key")) == {"y": 2.0, "x": 1.0}
4343

4444

tests/acceptance/conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def reset_caches():
1818
'default': {
1919
'cache': "aiocache.SimpleMemoryCache",
2020
'serializer': {
21-
'class': "aiocache.serializers.StringSerializer"
21+
'class': "aiocache.serializers.NullSerializer"
2222
}
2323
}
2424
})

tests/acceptance/test_base.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import asyncio
33

4-
from aiocache import serializers, RedisCache, SimpleMemoryCache, MemcachedCache
4+
from aiocache import RedisCache, SimpleMemoryCache, MemcachedCache
55
from aiocache.base import _Conn
66

77

@@ -14,7 +14,6 @@ class TestCache:
1414
@pytest.mark.asyncio
1515
async def test_setup(self, cache):
1616
assert cache.namespace == "test"
17-
assert isinstance(cache.serializer, serializers.StringSerializer)
1817

1918
@pytest.mark.asyncio
2019
async def test_get_missing(self, cache):
@@ -93,7 +92,7 @@ async def test_increment_missing(self, cache):
9392

9493
@pytest.mark.asyncio
9594
async def test_increment_existing(self, cache):
96-
await cache.set(pytest.KEY, "2")
95+
await cache.set(pytest.KEY, 2)
9796
assert await cache.increment(pytest.KEY, delta=2) == 4
9897
assert await cache.increment(pytest.KEY, delta=1) == 5
9998
assert await cache.increment(pytest.KEY, delta=-3) == 2

tests/acceptance/test_lock.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pytest
33

44
from aiocache._lock import _RedLock
5+
from aiocache.serializers import StringSerializer
56

67

78
@pytest.fixture
@@ -14,6 +15,7 @@ class TestRedLock:
1415
@pytest.mark.asyncio
1516
async def test_acquire(self, cache, lock):
1617
await lock.__aenter__()
18+
cache.serializer = StringSerializer()
1719
assert await cache.get(pytest.KEY + '-lock') == lock._value
1820

1921
@pytest.mark.asyncio

tests/acceptance/test_serializers.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from marshmallow import fields, Schema, post_load
1313

14-
from aiocache.serializers import StringSerializer, PickleSerializer, JsonSerializer
14+
from aiocache.serializers import NullSerializer, StringSerializer, PickleSerializer, JsonSerializer
1515

1616

1717
class MyType:
@@ -56,6 +56,30 @@ def loads(x):
5656
TYPES = [1, 2.0, "hi", True, ["1", 1], {"key": "value"}, MyType()]
5757

5858

59+
class TestNullSerializer:
60+
61+
@pytest.mark.parametrize("obj", TYPES)
62+
@pytest.mark.asyncio
63+
async def test_set_get_types(self, memory_cache, obj):
64+
memory_cache.serializer = NullSerializer()
65+
assert await memory_cache.set(pytest.KEY, obj) is True
66+
assert await memory_cache.get(pytest.KEY) is obj
67+
68+
@pytest.mark.parametrize("obj", TYPES)
69+
@pytest.mark.asyncio
70+
async def test_add_get_types(self, memory_cache, obj):
71+
memory_cache.serializer = NullSerializer()
72+
assert await memory_cache.add(pytest.KEY, obj) is True
73+
assert await memory_cache.get(pytest.KEY) is obj
74+
75+
@pytest.mark.parametrize("obj", TYPES)
76+
@pytest.mark.asyncio
77+
async def test_multi_set_multi_get_types(self, memory_cache, obj):
78+
memory_cache.serializer = NullSerializer()
79+
assert await memory_cache.multi_set([(pytest.KEY, obj)]) is True
80+
assert (await memory_cache.multi_get([pytest.KEY]))[0] is obj
81+
82+
5983
class TestStringSerializer:
6084

6185
@pytest.mark.parametrize("obj", TYPES)
@@ -132,6 +156,7 @@ class TestAltSerializers:
132156

133157
@pytest.mark.asyncio
134158
async def test_get_set_alt_serializer_functions(self, cache):
159+
cache.serializer = StringSerializer
135160
await cache.set(pytest.KEY, "value", dumps_fn=dumps)
136161
assert await cache.get(pytest.KEY) == "v4lu3"
137162
assert await cache.get(pytest.KEY, loads_fn=loads) == "value"

tests/ut/backends/test_memcached.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from aiocache import MemcachedCache
77
from aiocache.base import BaseCache
8+
from aiocache.serializers import JsonSerializer
89
from aiocache.backends.memcached import MemcachedBackend
910

1011

@@ -227,6 +228,9 @@ def set_test_namespace(self, memcached_cache):
227228
def test_inheritance(self):
228229
assert isinstance(MemcachedCache(), BaseCache)
229230

231+
def test_default_serializer(self):
232+
assert isinstance(MemcachedCache().serializer, JsonSerializer)
233+
230234
@pytest.mark.parametrize("namespace, expected", (
231235
[None, "test" + pytest.KEY],
232236
["", pytest.KEY],

tests/ut/backends/test_memory.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from aiocache import SimpleMemoryCache
77
from aiocache.base import BaseCache
8+
from aiocache.serializers import NullSerializer
89
from aiocache.backends.memory import SimpleMemoryBackend
910

1011

@@ -176,3 +177,6 @@ class TestSimpleMemoryCache:
176177

177178
def test_inheritance(self):
178179
assert isinstance(SimpleMemoryCache(), BaseCache)
180+
181+
def test_default_serializer(self):
182+
assert isinstance(SimpleMemoryCache().serializer, NullSerializer)

tests/ut/backends/test_redis.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from aiocache import RedisCache
77
from aiocache.base import BaseCache
8+
from aiocache.serializers import JsonSerializer
89
from aiocache.backends.redis import RedisBackend, conn
910

1011

@@ -332,6 +333,9 @@ def set_test_namespace(self, redis_cache):
332333
def test_inheritance(self):
333334
assert isinstance(RedisCache(), BaseCache)
334335

336+
def test_default_serializer(self):
337+
assert isinstance(RedisCache().serializer, JsonSerializer)
338+
335339
@pytest.mark.parametrize("namespace, expected", (
336340
[None, "test:" + pytest.KEY],
337341
["", pytest.KEY],

tests/ut/conftest.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from aiocache.base import BaseCache, API
55
from aiocache import caches, RedisCache, MemcachedCache
66
from aiocache.plugins import BasePlugin
7-
from aiocache.serializers import StringSerializer
7+
from aiocache.serializers import NullSerializer
88

99

1010
def pytest_namespace():
@@ -20,7 +20,7 @@ def reset_caches():
2020
'default': {
2121
'cache': "aiocache.SimpleMemoryCache",
2222
'serializer': {
23-
'class': "aiocache.serializers.StringSerializer"
23+
'class': "aiocache.serializers.NullSerializer"
2424
}
2525
}
2626
})
@@ -61,7 +61,7 @@ def mock_cache(mocker):
6161
mocker.spy(cache, cmd.__name__)
6262
mocker.spy(cache, "close")
6363
mocker.spy(cache, "_redlock")
64-
cache.serializer = asynctest.Mock(spec=StringSerializer)
64+
cache.serializer = asynctest.Mock(spec=NullSerializer)
6565
cache.plugins = [asynctest.Mock(spec=BasePlugin)]
6666
return cache
6767

tests/ut/test_decorators.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from aiocache.base import BaseCache
1111
from aiocache import cached, cached_stampede, multi_cached, SimpleMemoryCache
1212
from aiocache.decorators import _get_args_dict
13-
from aiocache.serializers import JsonSerializer
1413

1514

1615
async def stub(*args, value=None, seconds=0, **kwargs):
@@ -48,7 +47,7 @@ def test_init(self):
4847
assert c.key_from_attr == "key_attr"
4948
assert c.cache is None
5049
assert c._cache == SimpleMemoryCache
51-
assert c._serializer == JsonSerializer
50+
assert c._serializer is None
5251
assert c._kwargs == {'namespace': 'test'}
5352

5453
def test_fails_at_instantiation(self):
@@ -217,7 +216,7 @@ def test_init(self):
217216
assert c.key_from_attr == "key_attr"
218217
assert c.cache is None
219218
assert c._cache == SimpleMemoryCache
220-
assert c._serializer == JsonSerializer
219+
assert c._serializer is None
221220
assert c.lease == 3
222221
assert c._kwargs == {'namespace': 'test'}
223222

@@ -302,7 +301,7 @@ def test_init(self):
302301
assert mc.keys_from_attr == "keys"
303302
assert mc.cache is None
304303
assert mc._cache == SimpleMemoryCache
305-
assert mc._serializer == JsonSerializer
304+
assert mc._serializer is None
306305
assert mc._kwargs == {'namespace': 'test'}
307306

308307
def test_fails_at_instantiation(self):

0 commit comments

Comments
 (0)