--- /dev/null
+#coding: utf-8
+from django.core.cache import cache
+from django.utils.functional import wraps
+from cache_utils.utils import _cache_key, _func_info, _func_type
+
+def cached(timeout, group=None):
+ """ Caching decorator. Can be applied to function, method or classmethod.
+ Supports bulk cache invalidation and invalidation for exact parameter
+ set. Cache keys are human-readable because they are constructed from
+ callable's full name and arguments and then sanitized to make
+ memcached happy.
+
+ It can be used with or without group_backend. Without group_backend
+ bulk invalidation is not supported.
+
+ Wrapped callable gets `invalidate` methods. Call `invalidate` with
+ same arguments as function and the result for these arguments will be
+ invalidated.
+ """
+
+ backend_kwargs = {'group': group} if group else {}
+
+ def _cached(func):
+
+ func_type = _func_type(func)
+
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+
+ # full name is stored as attribute on first call
+ if not hasattr(wrapper, '_full_name'):
+ name, _args = _func_info(func, args)
+ wrapper._full_name = name
+
+ # try to get the value from cache
+ key = _cache_key(wrapper._full_name, func_type, args, kwargs)
+ value = cache.get(key, **backend_kwargs)
+
+ # in case of cache miss recalculate the value and put it to the cache
+ if value is None:
+ value = func(*args, **kwargs)
+ cache.set(key, value, timeout, **backend_kwargs)
+ return value
+
+ def invalidate(*args, **kwargs):
+ ''' invalidates cache result for function called with passed arguments '''
+ if not hasattr(wrapper, '_full_name'):
+ return
+ key = _cache_key(wrapper._full_name, 'function', args, kwargs)
+ cache.delete(key, **backend_kwargs)
+
+ wrapper.invalidate = invalidate
+ return wrapper
+ return _cached