SickGear/lib/diskcache/memo.py

105 lines
3.3 KiB
Python

"""Memoization utilities.
"""
from functools import wraps
from .core import ENOVAL
def memoize(cache, name=None, typed=False, expire=None, tag=None):
"""Memoizing cache decorator.
Decorator to wrap callable with memoizing function using cache. Repeated
calls with the same arguments will lookup result in cache and avoid
function evaluation.
If name is set to None (default), the callable name will be determined
automatically.
If typed is set to True, function arguments of different types will be
cached separately. For example, f(3) and f(3.0) will be treated as distinct
calls with distinct results.
The original underlying function is accessible through the __wrapped__
attribute. This is useful for introspection, for bypassing the cache, or
for rewrapping the function with a different cache.
>>> from diskcache import FanoutCache
>>> cache = FanoutCache('/tmp/diskcache/fanoutcache')
>>> @cache.memoize(typed=True, expire=1, tag='fib')
... def fibonacci(number):
... if number == 0:
... return 0
... elif number == 1:
... return 1
... else:
... return fibonacci(number - 1) + fibonacci(number - 2)
>>> print(sum(fibonacci(number=value) for value in range(100)))
573147844013817084100
Remember to call memoize when decorating a callable. If you forget, then a
TypeError will occur. Note the lack of parenthenses after memoize below:
>>> @cache.memoize
... def test():
... pass
Traceback (most recent call last):
...
TypeError: name cannot be callable
:param cache: cache to store callable arguments and return values
:param str name: name given for callable (default None, automatic)
:param bool typed: cache different types separately (default False)
:param float expire: seconds until arguments expire
(default None, no expiry)
:param str tag: text to associate with arguments (default None)
:return: callable decorator
"""
if callable(name):
raise TypeError('name cannot be callable')
def decorator(function):
"Decorator created by memoize call for callable."
if name is None:
try:
reference = function.__qualname__
except AttributeError:
reference = function.__name__
reference = function.__module__ + reference
else:
reference = name
reference = (reference,)
@wraps(function)
def wrapper(*args, **kwargs):
"Wrapper for callable to cache arguments and return values."
key = reference + args
if kwargs:
key += (ENOVAL,)
sorted_items = sorted(kwargs.items())
for item in sorted_items:
key += item
if typed:
key += tuple(type(arg) for arg in args)
if kwargs:
key += tuple(type(value) for _, value in sorted_items)
result = cache.get(key, default=ENOVAL, retry=True)
if result is ENOVAL:
result = function(*args, **kwargs)
cache.set(key, result, expire=expire, tag=tag, retry=True)
return result
return wrapper
return decorator