Useful cache decorators for methods and properties
Project description
Easy caching decorators
This package is intended to simplify caching and invalidation process in python-based (primarily) web applications. It’s possible to cache execution results of functions; instance, class and static methods; properties. Cache keys may be constructed in various different ways and may depend on any number of parameters.
The package supports tag-based cache invalidation and better works with Django, however any other frameworks can be used – see examples below.
Requirements
Library was tested in the following environments: * Python 2.7, 3.4 * Django 1.7, 1.8
Feel free to try it in yours, but it’s not guaranteed it will work. Submit an issue if you think it should.
Installation
pip install easy_cache
Introduction
Different ways to cache something
# classic way
from django.core.cache import cache
def time_consuming_operation(count):
cache_key = 'time_consuming_operation_{}'.format(count)
result = cache.get(cache_key, None)
if result is None:
# not found in cache
result = sum(range(count))
cache.set(cache_key, result, 3600)
return result
def invalidate_cache(count):
cache.delete('time_consuming_operation_{}'.format(count))
Now let’s take a look how easy_cache can help:
# easy way
from easy_cache import ecached
@ecached('time_consuming_operation_{count}', 3600)
def time_consuming_operation(count):
return sum(range(count))
def invalidate_cache(count):
time_consuming_operation.invalidate_cache_by_key(count)
Heart of the package is two decorators with the similar parameters:
ecached
Should be used to decorate any callable and cache returned result.
Parameters:
cache_key – cache key generator, default value is None so the key will be composed automatically based on function name and namespace. Also supports the following parameter types:
string – can contain Python advanced string formatting syntax, later this value will be formatted with dict of parameters provided to decorated function, see examples below.
sequence of strings – each string must be function parameter name.
callable – decorated function parameters will be passed to this callable and returned cache key will be used. Only two possible callable signatures are supported: callable(*args, **kwargs) and callable(meta), where meta is dict-like object with some additional fields – see below.
timeout – value will be cached with provided timeout, basically it should be number of seconds, however it depends on cache backend type. Default value is DEFAULT_VALUE – internal constant means that actually no value is provided to cache backend and thus backend should decide what timeout to use.
tags – sequence of strings or callable. Should provide or return list of tags added to cached value, so cache may be invalidated later with any tag name. Tag may support advanced string formatting syntax. See cache_key docs and examples for more details.
prefix – this parameter works both: as regular tag and also as cache key prefix, as usual advanced string formatting and callable are supported here.
cache_alias – cache backend alias name, it can also be Django cache backend alias name.
cache_instance – cache backend instance may be provided directly via this parameter.
ecached_property
Should be used to create so-called cached properties, has signature exactly the same as for ecached.
Simple examples
Code examples is the best way to show power of the package. Decorator can be simply used with default parameters only:
from easy_cache import ecached, create_cache_key
# default parameters, cache key will be generated automatically:
# <__module__>.<__class__>.<function name> + function parameters
# timeout will be default for specified cache backend
# "default" cache backend will be used if you use Django
@ecached()
def time_consuming_operation(*args, **kwargs):
pass
# simple static cache key and cache timeout 100 seconds
@ecached('time_consuming_operation', 100)
def time_consuming_operation():
pass
# cache key with advanced string formatting syntax
@ecached('key:{kwargs[param1]}:{kwargs[param2]}:{args[0]}')
def time_consuming_operation(*args, **kwargs):
pass
# use specific cache alias
from functools import partial
memcached = partial(ecached, cache_alias='memcached')
# equivalent to cache_key='{a}:{b}'
@memcached(['a', 'b'], timeout=600)
def time_consuming_operation(a, b, c='default'):
pass
# working with parameters provided to cached function
# cache key constructor must have the same signature as decorated function
def custom_cache_key(self, a, b, c, d):
return create_cache_key(self.id, a, d)
# working with `meta` object
def custom_cache_key_meta(meta):
return '{}:{}:{}'.format(meta['self'].id, meta['a'], meta['d'])
class A(object):
id = 1
@ecached(custom_cache_key)
def time_consuming_operation(self, a, b, c=10, d=20):
pass
@ecached(custom_cache_key_meta)
def time_consuming_opeartion(self, a, b, c=10, d=20):
pass
# How to cache static- and class-methods correctly
class B(object):
# cache only for each different year
@ecached(lambda start_date: 'get_list:{}'.format(start_date.year))
@staticmethod
def get_list_by_date(start_date):
pass
CONST = 'abc'
@ecached('info_cache:{cls.CONST}', 3600, cache_alias='redis_cache')
@classmethod
def get_info(arg):
pass
MetaCallable object description
Meta object has the following parameters:
args – tuple with positional arguments provided to decorated function
kwargs – dictionary with keyword arguments provided to decorated function
returned_value – value returned from decorated function, available only when meta object is handled in tags or prefix constructors. Before using this parameter you have to check has_returned_value property: python def f(meta): if meta.has_returned_value: # ... do something with meta.returned_value ...
call_args - dictionary with all positional and keyword arguments provided to decorated function, you may also access them via __getitem__ dict interface, e. g. meta['param1'].
Prefix usage
Commonly prefix is used to invalidate all cache-keys in one namespace, e. g.:
from functools import partial
class Shop(models.Model):
single_shop_cache = partial(ecached, prefix='shop:{self.id}')
@single_shop_cache('goods_list')
def get_all_goods_list(self):
return [...]
@single_shop_cache('prices_list')
def get_all_prices_list(self):
return [...]
# if you have `shop` object you are able to use the following invalidation
# strategies:
# Invalidate cached list of goods for concrete shop
Shop.get_all_goods_list.invalidate_cache_by_key(shop)
# Invalidate cached list of prices for concrete shop
Shop.get_all_prices_list.invalidate_cache_by_key(shop)
# Invalidate all cached items for concrete shop
Shop.get_all_goods_list.invalidate_cache_by_prefix(shop)
# or
Shop.get_all_prices_list.invalidate_cache_by_prefix(shop)
# or
from easy_cache import invalidate_cache_prefix
invalidate_cache_prefix('shop:{self.id}'.format(self=shop))
Invalidation summary
There are two ways to invalidate cache objects: use ivalidation methods bound to decorated function and separate functions-invalidators.
<decorated>.invalidate_cache_by_key(*args, **kwargs)
<decorated>.invalidate_cache_by_tags(tags=(), *args, **kwargs)
<decorated>.invalidate_cache_by_prefix(*args, **kwargs)
# <decorated> should be used with class instance if it is used in class namespace:
class A:
@ecached()
def method(self):
pass
@ecached_property()
def obj_property(self):
pass
A.method.invalidate_cache_by_key()
# or
A().method.invalidate_cache_by_key()
# only one variant is possible for a property
A.obj_property.invalidate_cache_by_key()
# and
from easy_cache import (
invalidate_cache_key,
invalidate_cache_tags,
invalidate_cache_prefix,
create_cache_key,
)
invalidate_cache_key(cache_key, cache_instance=None, cache_alias=None)
invalidate_cache_tags(tags, cache_instance=None, cache_alias=None)
invalidate_cache_prefix(prefix, cache_instance=None, cache_alias=None)
Here tags can be as string (single tag) or list of tags. Bound methods should be provided with parameters if they are used in cache key/tag/prefix:
@ecached('key:{a}:value:{c}', tags=['tag:{a}'], prefix='pre:{b}', cache_alias='memcached')
def time_consuming_operation(a, b, c=100):
pass
time_consuming_operation.invalidate_cache_by_key(1, 2, c=11)
time_consuming_operation.invalidate_cache_by_tags(a=10)
time_consuming_operation.invalidate_cache_by_prefix(b=2)
# or using `create_cache_key` helper
invalidate_cache_key(
create_cache_key('key', 1, 'value', 11), cache_alias='memcached'
)
invalidate_cache_tags(create_cache_key('tag', 10), cache_alias='memcached')
invalidate_cache_prefix('pre:{}'.format(2))
Performance
TBA
Internal cache framework
TBA
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.