After doing a lot of research I found the cachetools
library and this pull request.
I just took the solution from there: this is an implementation of asyncio support for cachetools
. And cachetools
already allows you to pass a key parameter that allows you to chose which parameters of the function will be involved in the hashing.
import functools
import inspect
import logging
from cachetools.keys import hashkey
logger = logging.getLogger(__name__)
class NullContext(object):
"""A class for noop context managers."""
def __enter__(self):
"""Return ``self`` upon entering the runtime context."""
return self
def __exit__(self, exc_type, exc_value, traceback):
"""Raise any exception triggered within the runtime context."""
async def __aenter__(self):
"""Return ``self`` upon entering the runtime context."""
return self
async def __aexit__(self, exc_type, exc_value, traceback):
"""Raise any exception triggered within the runtime context."""
def aiocached(cache, key=hashkey, lock=None):
"""Decorator to wrap a function or a coroutine with a memoizing callable.
When ``lock`` is provided for a standard function, it's expected to
implement ``__enter__`` and ``__exit__`` that will be used to lock
the cache when it gets updated. If it wraps a coroutine, ``lock``
must implement ``__aenter__`` and ``__aexit__``.
"""
lock = lock or NullContext()
def decorator(func):
if not inspect.iscoroutinefunction(func):
raise RuntimeError('Use aiocached only with async functions')
async def wrapper(*args, **kwargs):
fk = key(*args, **kwargs)
async with lock:
fval = cache.get(fk)
# cache hit
if fval is not None:
return fval
# cache miss
fval = await func(*args, **kwargs)
try:
async with lock:
cache[fk] = fval
except ValueError:
logger.debug('Failed to cache {0}'.format(fval))
return fval
return functools.wraps(func)(wrapper)
return decorator
Now you can use the aiocached
decorator just like cached