6
from decorator import decorator
7
from paste.deploy.converters import asbool
9
from pylons.decorators.util import get_pylons
11
log = logging.getLogger(__name__)
13
def beaker_cache(key="cache_default", expire="never", type=None,
15
cache_headers=('content-type', 'content-length'),
16
invalidate_on_startup=False,
17
cache_response=True, **b_kwargs):
18
"""Cache decorator utilizing Beaker. Caches action or other
19
function that returns a pickle-able object as a result.
24
None - No variable key, uses function name as key
25
"cache_default" - Uses all function arguments as the key
26
string - Use kwargs[key] as key
27
list - Use [kwargs[k] for k in list] as key
29
Time in seconds before cache expires, or the string "never".
32
Type of cache to use: dbm, memory, file, memcached, or None for
35
Uses the query arguments as the key, defaults to False
37
A tuple of header names indicating response headers that
39
``invalidate_on_startup``
40
If True, the cache will be invalidated each time the application
41
starts or is restarted.
43
Determines whether the response at the time beaker_cache is used
44
should be cached or not, defaults to True.
47
When cache_response is set to False, the cache_headers
48
argument is ignored as none of the response is cached.
50
If cache_enabled is set to False in the .ini file, then cache is
54
if invalidate_on_startup:
55
starttime = time.time()
58
cache_headers = set(cache_headers)
60
def wrapper(func, *args, **kwargs):
61
"""Decorator wrapper"""
62
pylons = get_pylons(args)
63
log.debug("Wrapped with key: %s, expire: %s, type: %s, query_args: %s",
64
key, expire, type, query_args)
65
enabled = pylons.config.get("cache_enabled", "True")
66
if not asbool(enabled):
67
log.debug("Caching disabled, skipping cache lookup")
68
return func(*args, **kwargs)
71
key_dict = kwargs.copy()
72
key_dict.update(_make_dict_from_args(func, args))
74
key_dict.update(pylons.request.GET.mixed())
76
if key != "cache_default":
77
if isinstance(key, list):
78
key_dict = dict((k, key_dict[k]) for k in key)
80
key_dict = {key: key_dict[key]}
87
namespace, cache_key = create_cache_key(func, key_dict, self)
90
b_kwargs['type'] = type
92
cache_obj = getattr(pylons.app_globals, 'cache', None)
94
cache_obj = getattr(pylons, 'cache', None)
96
raise Exception('No CacheMiddleware or cache object on '
97
' app_globals was found')
99
my_cache = cache_obj.get_cache(namespace, **b_kwargs)
101
if expire == "never":
104
cache_expire = expire
107
log.debug("Creating new cache copy with key: %s, type: %s",
109
result = func(*args, **kwargs)
110
glob_response = pylons.response
111
headers = glob_response.headerlist
112
status = glob_response.status
113
full_response = dict(headers=headers, status=status,
114
cookies=None, content=result)
117
response = my_cache.get_value(cache_key, createfunc=create_func,
118
expiretime=cache_expire,
121
glob_response = pylons.response
122
glob_response.headerlist = [header for header in response['headers']
123
if header[0].lower() in cache_headers]
124
glob_response.status = response['status']
126
return response['content']
127
return decorator(wrapper)
129
def create_cache_key(func, key_dict=None, self=None):
130
"""Get a cache namespace and key used by the beaker_cache decorator.
133
from pylons import cache
134
from pylons.decorators.cache import create_cache_key
135
namespace, key = create_cache_key(MyController.some_method)
136
cache.get_cache(namespace).remove(key)
140
if hasattr(func, 'im_func'):
143
cache_key = func.__name__
145
cache_key = func.__name__
147
cache_key += " " + " ".join("%s=%s" % (k, v)
148
for k, v in key_dict.iteritems())
151
kls = getattr(self, '__class__', None)
154
return '%s.%s' % (kls.__module__, kls.__name__), cache_key
156
return func.__module__, cache_key
158
def _make_dict_from_args(func, args):
159
"""Inspects function for name of args"""
161
for i, arg in enumerate(inspect.getargspec(func)[0]):
163
args_keys[arg] = args[i]