anfema_django_utils.caching.cached_call

cached_call(maxsize=128)

Decorator to wrap a function with a memoizing callable similar to the functools.lru_cache() decorator.

You can use the decorator on lambdas, functions, methods, classmethods and staticmethods.

Note

Some callables may not be cachable in certain implementations of Python. For example, in CPython, built-in functions defined in C provide no metadata about their arguments.

Unlike functools.lru_cache(), cached_call() provides the possibility to invalidate the cache for a specific call. The cache keeps references to the arguments and return values until they age out of the cache or until the cache is cleared via clear_call() or clear().

To cache the result cached_call() uses the positional and keyword arguments and creates a hash value using the super_hash library. Hence, the positional and keyword arguments must be hashable with super_hash.

If a method is cached, the self instance attribute is included in the cache. The same applies for the cls attribute of a cached classmethod.

Distinct argument patterns does not result in separate cache entries, i.e. f(a=1, b=2) and f(b=2, a=1) have the same cache entry. You should consider that arguments with a default value are taken also into account for caching. I.e. a function with a signature f(a=None, b=True) results in the same cache entry for the calls f(a=None) and f(b=True). For methods, the calls Foo.bar(foo, *args, **kwargs) and foo.bar(*args, **kwargs) will be considered the same and thus also result in the same cache entry.

If user_function is specified, it must be a callable. This allows the cached_call() decorator to be applied directly to a user function, leaving the maxsize at its default value of 128:

@cached_call(maxsize=32)
def foo(*args):
    ...

@cached_call
def bar(*args):
    ...

If maxsize is set to None, the cache can grow without bound.

The original underlying callable is accessible through the __wrapped__ attribute.

To clear a cache entry for a specific call you can use clear_call() with passing the arguments, for which call the cache entry shall be cleared. To reset the complete cache, call clear():

@cached_call
def foo(*args):
    return uuid.uuid4()

>>> foo(1)  # returns a new UUID
UUID('9306c73b-ff0a-439b-901c-c347ac548904')
>>> foo(1)  # returns the cached value
UUID('9306c73b-ff0a-439b-901c-c347ac548904')
>>> foo(2)  # returns a new UUID
UUID('2e7ca126-7943-46cc-a81b-0fbd30b07969')
>>> foo.clear_call(1)  # clear the cache only for the call foo(1)

>>> foo(1)  # returns a new UUID
UUID('3b3cc8b2-6ec5-4582-8314-926aa625b1fc')
>>> foo(2)  # returns the cached value
UUID('2e7ca126-7943-46cc-a81b-0fbd30b07969')
>>> foo.clear()  # clear the cache for all calls