104 lines
3.2 KiB
Python
104 lines
3.2 KiB
Python
"""
|
|
Utilities related to caching.
|
|
"""
|
|
import collections
|
|
import cPickle as pickle
|
|
import functools
|
|
import zlib
|
|
from xblock.core import XBlock
|
|
|
|
|
|
def memoize_in_request_cache(request_cache_attr_name=None):
|
|
"""
|
|
Memoize a method call's results in the request_cache if there's one. Creates the cache key by
|
|
joining the unicode of all the args with &; so, if your arg may use the default &, it may
|
|
have false hits.
|
|
|
|
Arguments:
|
|
request_cache_attr_name - The name of the field or property in this method's containing
|
|
class that stores the request_cache.
|
|
"""
|
|
def _decorator(func):
|
|
"""Outer method decorator."""
|
|
@functools.wraps(func)
|
|
def _wrapper(self, *args, **kwargs):
|
|
"""
|
|
Wraps a method to memoize results.
|
|
"""
|
|
request_cache = getattr(self, request_cache_attr_name, None)
|
|
if request_cache:
|
|
cache_key = '&'.join([hashvalue(arg) for arg in args])
|
|
if cache_key in request_cache.data.setdefault(func.__name__, {}):
|
|
return request_cache.data[func.__name__][cache_key]
|
|
|
|
result = func(self, *args, **kwargs)
|
|
|
|
request_cache.data[func.__name__][cache_key] = result
|
|
return result
|
|
else:
|
|
return func(self, *args, **kwargs)
|
|
return _wrapper
|
|
return _decorator
|
|
|
|
|
|
class memoized(object): # pylint: disable=invalid-name
|
|
"""
|
|
Decorator. Caches a function's return value each time it is called.
|
|
If called later with the same arguments, the cached value is returned
|
|
(not reevaluated).
|
|
https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
|
|
|
|
WARNING: Only use this memoized decorator for caching data that
|
|
is constant throughout the lifetime of a gunicorn worker process,
|
|
is costly to compute, and is required often. Otherwise, it can lead to
|
|
unwanted memory leakage.
|
|
"""
|
|
|
|
def __init__(self, func):
|
|
self.func = func
|
|
self.cache = {}
|
|
|
|
def __call__(self, *args):
|
|
if not isinstance(args, collections.Hashable):
|
|
# uncacheable. a list, for instance.
|
|
# better to not cache than blow up.
|
|
return self.func(*args)
|
|
if args in self.cache:
|
|
return self.cache[args]
|
|
else:
|
|
value = self.func(*args)
|
|
self.cache[args] = value
|
|
return value
|
|
|
|
def __repr__(self):
|
|
"""
|
|
Return the function's docstring.
|
|
"""
|
|
return self.func.__doc__
|
|
|
|
def __get__(self, obj, objtype):
|
|
"""
|
|
Support instance methods.
|
|
"""
|
|
return functools.partial(self.__call__, obj)
|
|
|
|
|
|
def hashvalue(arg):
|
|
"""
|
|
If arg is an xblock, use its location. otherwise just turn it into a string
|
|
"""
|
|
if isinstance(arg, XBlock):
|
|
return unicode(arg.location)
|
|
else:
|
|
return unicode(arg)
|
|
|
|
|
|
def zpickle(data):
|
|
"""Given any data structure, returns a zlib compressed pickled serialization."""
|
|
return zlib.compress(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))
|
|
|
|
|
|
def zunpickle(zdata):
|
|
"""Given a zlib compressed pickled serialization, returns the deserialized data."""
|
|
return pickle.loads(zlib.decompress(zdata))
|