Lets start from the end.
@cache('1h') def func1(user_id): return heavy_compute(user_id) result = func1(123)
Now our decorated func1 has some very useful methods!
To get value strictly from redis (do not compute, even if it absent in redis) we can use .get() method:
result_from_redis = func1.get(123)
To set (update if already exists) value to redis we can use .update() method. It will recompute value and save it in redis:
newly_computed_result = func1.update(123)
To save in redis value we already have (not to compute it) we can use .update_manually() method. It accepts func1 args + value to save (last arg):
func1.update_manually(123, 'winner') # 123: user_id # 'winner': our result to save in redis
To delete result from redis we can use .delete() method:
deleted_count = func1.delete(123)
To get redis key we can use .get_key() method:
key_in_redis = func1.get_key(123)
Here full code for this cache decorator:
import json from functools import partial, wraps redis = Redis() # for example Flask-Redis class null = object() def _get_cache_key(func, *args): result = '%s.%s:' % (func.__module__, func.__name__) if args: result = '%s%s' % (result, str(args)) return result def _get_cache(func, *args): value = redis.client.get(_get_cache_key(func, *args)) if value is not None: return json.loads(value) return null def _update_cache(func, *args): value = func(*args) redis.client.set( name=_get_cache_key(func, *args), value=json.dumps(value), ex=func.timeout, ) return value def _update_cache_manually(func, *args_with_value_as_last): redis.client.set( name=_get_cache_key(func, *args_with_value_as_last[:-1]), value=json.dumps(args_with_value_as_last[-1]), ex=func.timeout, ) return args_with_value_as_last[-1] def _delete_cache(func, *args): return redis.client.delete(_get_cache_key(func, *args)) _MAP = dict( s=1, # seconds m=60, # minutes h=60 * 60, # hours d=60 * 60 * 24, # days w=60 * 60 * 24 * 7, # weeks ) def cache(timeout): """@timeout: 60, '40s', '5m', '1h', '2d', '3w'""" def wrapper(func): if isinstance(timeout, int): seconds = timeout elif timeout[-1] in _MAP: # last letter [s, m, h, d, w] seconds = int(timeout.rstrip(timeout[-1])) * _MAP[timeout[-1]] else: raise ValueError('Unknown format for timeout `%s`' % timeout) func.timeout = seconds @wraps(func) def func_wrapped(*args): value = _get_cache(func, *args) if value is not null: return value return _update_cache(func, *args) func_wrapped.timeout = func.timeout func_wrapped.get_key = partial(_get_cache_key, func) func_wrapped.get = partial(_get_cache, func) func_wrapped.update = partial(_update_cache, func) func_wrapped.update_manually = partial(_update_cache_manually, func) func_wrapped.delete = partial(_delete_cache, func) return func_wrapped return wrapper
No comments:
Post a Comment