frappe_docker/frappe-bench/env/lib/python2.7/site-packages/premailer/cache.py
2017-07-31 15:51:51 +05:30

85 lines
2.2 KiB
Python

import functools
class _HashedSeq(list):
# # From CPython
__slots__ = 'hashvalue'
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
# if we only have nonlocal
class _Cache(object):
def __init__(self):
self.off = False
self.missed = 0
self.cache = {}
def function_cache(expected_max_entries=1000):
"""
function_cache is a decorator for caching function call
the argument to the wrapped function must be hashable else
it will not work
expected_max_entries is for protecting cache failure. If cache
misses more than this number the cache will turn off itself.
Specify None you sure that the cache will not cause memory
limit problem.
Args:
expected_max_entries(integer OR None): will raise if not correct
Returns:
function
"""
if (
expected_max_entries is not None and
not isinstance(expected_max_entries, int)
):
raise TypeError(
'Expected expected_max_entries to be an integer or None'
)
# indicator of cache missed
sentinel = object()
def decorator(func):
cached = _Cache()
@functools.wraps(func)
def inner(*args, **kwargs):
if cached.off:
return func(*args, **kwargs)
keys = args
if kwargs:
sorted_items = sorted(kwargs.items())
for item in sorted_items:
keys += item
hashed = hash(_HashedSeq(keys))
result = cached.cache.get(hashed, sentinel)
if result is sentinel:
cached.missed += 1
result = func(*args, **kwargs)
cached.cache[hashed] = result
# # something is wrong if we are here more than expected
# # empty and turn it off
if (
expected_max_entries is not None and
cached.missed > expected_max_entries
):
cached.off = True
cached.cache.clear()
return result
return inner
return decorator