Skip to content

Commit

Permalink
Add key_builder param to caches to customize keys
Browse files Browse the repository at this point in the history
  • Loading branch information
argaen committed Jul 23, 2017
1 parent 9495ec7 commit 17c8f44
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 21 deletions.
23 changes: 13 additions & 10 deletions aiocache/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,18 @@ class BaseCache:
list.
:param namespace: string to use as default prefix for the key used in all operations of
the backend. Default is None
:param key_builder: alternative callable to build the key. Receives the key and the namespace as
params and should return something that can be used as key by the underlying backend.
:param timeout: int or float in seconds specifying maximum timeout for the operations to last.
By default its 5. Use 0 or None if you want to disable it.
"""

def __init__(
self, serializer=None, plugins=None,
namespace=None, timeout=5):
namespace=None, key_builder=None, timeout=5):
self.timeout = timeout
self.namespace = namespace
self.build_key = key_builder or self._build_key

self._serializer = None
self.serializer = serializer or serializers.StringSerializer()
Expand Down Expand Up @@ -148,7 +151,7 @@ async def add(self, key, value, ttl=None, dumps_fn=None, namespace=None, _conn=N
"""
start = time.monotonic()
dumps = dumps_fn or self._serializer.dumps
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)

await self._add(ns_key, dumps(value), ttl, _conn=_conn)

Expand Down Expand Up @@ -177,7 +180,7 @@ async def get(self, key, default=None, loads_fn=None, namespace=None, _conn=None
"""
start = time.monotonic()
loads = loads_fn or self._serializer.loads
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)

value = loads(await self._get(ns_key, encoding=self.serializer.encoding, _conn=_conn))

Expand Down Expand Up @@ -206,7 +209,7 @@ async def multi_get(self, keys, loads_fn=None, namespace=None, _conn=None):
start = time.monotonic()
loads = loads_fn or self._serializer.loads

ns_keys = [self._build_key(key, namespace=namespace) for key in keys]
ns_keys = [self.build_key(key, namespace=namespace) for key in keys]
values = [loads(value) for value in await self._multi_get(
ns_keys, encoding=self.serializer.encoding, _conn=_conn)]

Expand Down Expand Up @@ -243,7 +246,7 @@ async def set(
"""
start = time.monotonic()
dumps = dumps_fn or self._serializer.dumps
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)

res = await self._set(ns_key, dumps(value), ttl=ttl, _cas_token=_cas_token, _conn=_conn)

Expand Down Expand Up @@ -277,7 +280,7 @@ async def multi_set(self, pairs, ttl=None, dumps_fn=None, namespace=None, _conn=

tmp_pairs = []
for key, value in pairs:
tmp_pairs.append((self._build_key(key, namespace=namespace), dumps(value)))
tmp_pairs.append((self.build_key(key, namespace=namespace), dumps(value)))

await self._multi_set(tmp_pairs, ttl, _conn=_conn)

Expand Down Expand Up @@ -307,7 +310,7 @@ async def delete(self, key, namespace=None, _conn=None):
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
"""
start = time.monotonic()
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)
ret = await self._delete(ns_key, _conn=_conn)
logger.debug("DELETE %s %d (%.4f)s", ns_key, ret, time.monotonic() - start)
return ret
Expand All @@ -331,7 +334,7 @@ async def exists(self, key, namespace=None, _conn=None):
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
"""
start = time.monotonic()
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)
ret = await self._exists(ns_key, _conn=_conn)
logger.debug("EXISTS %s %d (%.4f)s", ns_key, ret, time.monotonic() - start)
return ret
Expand All @@ -358,7 +361,7 @@ async def increment(self, key, delta=1, namespace=None, _conn=None):
:raises: :class:`TypeError` if value is not incrementable
"""
start = time.monotonic()
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)
ret = await self._increment(ns_key, delta, _conn=_conn)
logger.debug("INCREMENT %s %d (%.4f)s", ns_key, ret, time.monotonic() - start)
return ret
Expand All @@ -383,7 +386,7 @@ async def expire(self, key, ttl, namespace=None, _conn=None):
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
"""
start = time.monotonic()
ns_key = self._build_key(key, namespace=namespace)
ns_key = self.build_key(key, namespace=namespace)
ret = await self._expire(ns_key, ttl, _conn=_conn)
logger.debug("EXPIRE %s %d (%.4f)s", ns_key, ret, time.monotonic() - start)
return ret
Expand Down
1 change: 1 addition & 0 deletions requirements-ci.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-r requirements.txt
-e .
flake8==3.3.0
mypy==0.511
pytest==3.1.3
pytest-asyncio==0.6.0
pytest-cov==2.5.1
Expand Down
8 changes: 4 additions & 4 deletions tests/acceptance/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ async def test_cached_stampede(self, mocker, cache):
decorator = cached_stampede(ttl=10, lease=2)

await asyncio.gather(
decorator(stub)(1),
decorator(stub)(1))
decorator(stub)(0.5),
decorator(stub)(0.5))

cache.get.assert_called_with('acceptance.test_decoratorsstub(1,)[]')
cache.get.assert_called_with('acceptance.test_decoratorsstub(0.5,)[]')
assert cache.get.call_count == 4
cache.set.assert_called_with(
'acceptance.test_decoratorsstub(1,)[]', mock.ANY, ttl=10)
'acceptance.test_decoratorsstub(0.5,)[]', mock.ANY, ttl=10)
assert cache.set.call_count == 1

@pytest.mark.asyncio
Expand Down
6 changes: 3 additions & 3 deletions tests/ut/backends/test_memcached.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,10 @@ def test_default_serializer(self):
["my_ns", "my_ns" + pytest.KEY],)
)
def test_build_key_bytes(self, set_test_namespace, memcached_cache, namespace, expected):
assert memcached_cache._build_key(pytest.KEY, namespace=namespace) == expected.encode()
assert memcached_cache.build_key(pytest.KEY, namespace=namespace) == expected.encode()

def test_build_key_no_namespace(self, memcached_cache):
assert memcached_cache._build_key(pytest.KEY, namespace=None) == pytest.KEY.encode()
assert memcached_cache.build_key(pytest.KEY, namespace=None) == pytest.KEY.encode()

def test_build_key_no_spaces(self, memcached_cache):
assert memcached_cache._build_key('hello world') == b'hello_world'
assert memcached_cache.build_key('hello world') == b'hello_world'
4 changes: 2 additions & 2 deletions tests/ut/backends/test_redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ def test_default_serializer(self):
["my_ns", "my_ns:" + pytest.KEY],)
)
def test_build_key_double_dot(self, set_test_namespace, redis_cache, namespace, expected):
assert redis_cache._build_key(pytest.KEY, namespace=namespace) == expected
assert redis_cache.build_key(pytest.KEY, namespace=namespace) == expected

def test_build_key_no_namespace(self, redis_cache):
assert redis_cache._build_key(pytest.KEY, namespace=None) == pytest.KEY
assert redis_cache.build_key(pytest.KEY, namespace=None) == pytest.KEY
8 changes: 6 additions & 2 deletions tests/ut/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from asynctest import patch, MagicMock, ANY, CoroutineMock

from aiocache.base import API, _Conn
from aiocache.base import API, _Conn, BaseCache


class TestAPI:
Expand Down Expand Up @@ -225,7 +225,11 @@ def set_test_namespace(self, base_cache):
["my_ns", "my_ns" + pytest.KEY],)
)
def test_build_key(self, set_test_namespace, base_cache, namespace, expected):
assert base_cache._build_key(pytest.KEY, namespace=namespace) == expected
assert base_cache.build_key(pytest.KEY, namespace=namespace) == expected

def test_alt_build_key(self):
cache = BaseCache(key_builder=lambda key, namespace: 'x')
assert cache.build_key(pytest.KEY, 'namespace') == 'x'


class TestCache:
Expand Down

0 comments on commit 17c8f44

Please sign in to comment.