Decorators

aiocache comes with a couple of decorators for caching results from asynchronous functions. Do not use the decorator in synchronous functions, it may lead to unexpected behavior.

cached

class aiocache.cached(cache, *, ttl=<object object>, key_builder=None, skip_cache_func=<function cached.<lambda>>, noself=False)[source]

Caches the functions return value into a key generated with module_name, function_name and args. The cache is available in the function object as <function_name>.cache.

Parameters:
  • cache – cache instance to use when calling the set/get operations.

  • ttl – int seconds to store the function call. Default is None which means no expiration.

  • key_builder – Callable that allows to build the function dynamically. It receives the function plus same args and kwargs passed to the function. This behavior is necessarily different than BaseCache.build_key()

  • skip_cache_func – Callable that receives the result after calling the wrapped function and should return True if the value should skip the cache (or False to store in the cache). e.g. to avoid caching None results: lambda r: r is None

  • noself – bool if you are decorating a class function, by default self is also used to generate the key. This will result in same function calls done by different class instances to use different cache keys. Use noself=True if you want to ignore it.

 1import asyncio
 2
 3from collections import namedtuple
 4import redis.asyncio as redis
 5
 6from aiocache import cached
 7from aiocache import RedisCache
 8from aiocache.serializers import PickleSerializer
 9
10Result = namedtuple('Result', "content, status")
11
12cache = RedisCache(namespace="main", client=redis.Redis(), serializer=PickleSerializer())
13
14
15@cached(cache, ttl=10, key_builder=lambda *args, **kw: "key")
16async def cached_call():
17    return Result("content", 200)
18
19
20async def test_cached():
21    async with cache:
22        await cached_call()
23        exists = await cache.exists("key")
24        assert exists is True
25        await cache.delete("key")
26
27
28if __name__ == "__main__":
29    asyncio.run(test_cached())

multi_cached

class aiocache.multi_cached(cache=None, *, keys_from_attr, key_builder=None, skip_cache_func=<function multi_cached.<lambda>>, ttl=<object object>)[source]

Only supports functions that return dict-like structures. This decorator caches each key/value of the dict-like object returned by the function. The dict keys of the returned data should match the set of keys that are passed to the decorated callable in an iterable object. The name of that argument is passed to this decorator via the parameter keys_from_attr. keys_from_attr can be the name of a positional or keyword argument.

If the argument specified by keys_from_attr is an empty list, the cache will be ignored and the function will be called. If only some of the keys in keys_from_attr``are cached (and ``cache_read is True) those values will be fetched from the cache, and only the uncached keys will be passed to the callable via the argument specified by keys_from_attr.

By default, the callable’s name and call signature are not incorporated into the cache key, so if there is another cached function returning a dict with same keys, those keys will be overwritten. To avoid this, use a specific namespace in each cache decorator or pass a key_builder.

If key_builder is passed, then the values of keys_from_attr will be transformed before requesting them from the cache. Equivalently, the keys in the dict-like mapping returned by the decorated callable will be transformed before storing them in the cache.

The cache is available in the function object as <function_name>.cache.

Extra args that are injected in the function that you can use to control the cache behavior are:

  • cache_read: Controls whether the function call will try to read from cache first or

    not. Enabled by default.

  • cache_write: Controls whether the function call will try to write in the cache once

    the result has been retrieved. Enabled by default.

  • aiocache_wait_for_write: Controls whether the call of the function will wait for the

    value in the cache to be written. If set to False, the write happens in the background. Enabled by default

Parameters:
  • cache – cache instance to use when calling the multi_set/multi_get operations.

  • keys_from_attr – name of the arg or kwarg in the decorated callable that contains an iterable that yields the keys returned by the decorated callable.

  • key_builder – Callable that enables mapping the decorated function’s keys to the keys used by the cache. Receives a key from the iterable corresponding to keys_from_attr, the decorated callable, and the positional and keyword arguments that were passed to the decorated callable. This behavior is necessarily different than BaseCache.build_key() and the call signature differs from cached.key_builder.

  • skip_cache_func – Callable that receives both key and value and returns True if that key-value pair should not be cached (or False to store in cache). The keys and values to be passed are taken from the wrapped function result.

  • ttl – int seconds to store the keys. Default is 0 which means no expiration.

 1import asyncio
 2
 3import redis.asyncio as redis
 4
 5from aiocache import multi_cached
 6from aiocache import RedisCache
 7
 8DICT = {
 9    'a': "Z",
10    'b': "Y",
11    'c': "X",
12    'd': "W"
13}
14
15cache = RedisCache(namespace="main", client=redis.Redis())
16
17
18@multi_cached(cache, keys_from_attr="ids")
19async def multi_cached_ids(ids=None):
20    return {id_: DICT[id_] for id_ in ids}
21
22
23@multi_cached(cache, keys_from_attr="keys")
24async def multi_cached_keys(keys=None):
25    return {id_: DICT[id_] for id_ in keys}
26
27
28async def test_multi_cached():
29    await multi_cached_ids(ids=("a", "b"))
30    await multi_cached_ids(ids=("a", "c"))
31    await multi_cached_keys(keys=("d",))
32
33    assert await cache.exists("a")
34    assert await cache.exists("b")
35    assert await cache.exists("c")
36    assert await cache.exists("d")
37
38    await cache.delete("a")
39    await cache.delete("b")
40    await cache.delete("c")
41    await cache.delete("d")
42    await cache.close()
43
44
45if __name__ == "__main__":
46    asyncio.run(test_multi_cached())