1 # mako/cache.py 2 # Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file> 3 # 4 # This module is part of Mako and is released under 5 # the MIT License: http://www.opensource.org/licenses/mit-license.php 6 7 from mako import compat, util 8 9 _cache_plugins = util.PluginLoader("mako.cache") 10 11 register_plugin = _cache_plugins.register 12 register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl") 13 14 15 class Cache(object): 16 """Represents a data content cache made available to the module 17 space of a specific :class:`.Template` object. 18 19 .. versionadded:: 0.6 20 :class:`.Cache` by itself is mostly a 21 container for a :class:`.CacheImpl` object, which implements 22 a fixed API to provide caching services; specific subclasses exist to 23 implement different 24 caching strategies. Mako includes a backend that works with 25 the Beaker caching system. Beaker itself then supports 26 a number of backends (i.e. file, memory, memcached, etc.) 27 28 The construction of a :class:`.Cache` is part of the mechanics 29 of a :class:`.Template`, and programmatic access to this 30 cache is typically via the :attr:`.Template.cache` attribute. 31 32 """ 33 34 impl = None 35 """Provide the :class:`.CacheImpl` in use by this :class:`.Cache`. 36 37 This accessor allows a :class:`.CacheImpl` with additional 38 methods beyond that of :class:`.Cache` to be used programmatically. 39 40 """ 41 42 id = None 43 """Return the 'id' that identifies this cache. 44 45 This is a value that should be globally unique to the 46 :class:`.Template` associated with this cache, and can 47 be used by a caching system to name a local container 48 for data specific to this template. 49 50 """ 51 52 starttime = None 53 """Epochal time value for when the owning :class:`.Template` was 54 first compiled. 55 56 A cache implementation may wish to invalidate data earlier than 57 this timestamp; this has the effect of the cache for a specific 58 :class:`.Template` starting clean any time the :class:`.Template` 59 is recompiled, such as when the original template file changed on 60 the filesystem. 61 62 """ 63 64 def __init__(self, template, *args): 65 # check for a stale template calling the 66 # constructor 67 if isinstance(template, compat.string_types) and args: 68 return 69 self.template = template 70 self.id = template.module.__name__ 71 self.starttime = template.module._modified_time 72 self._def_regions = {} 73 self.impl = self._load_impl(self.template.cache_impl) 74 75 def _load_impl(self, name): 76 return _cache_plugins.load(name)(self) 77 78 def get_or_create(self, key, creation_function, **kw): 79 """Retrieve a value from the cache, using the given creation function 80 to generate a new value.""" 81 82 return self._ctx_get_or_create(key, creation_function, None, **kw) 83 84 def _ctx_get_or_create(self, key, creation_function, context, **kw): 85 """Retrieve a value from the cache, using the given creation function 86 to generate a new value.""" 87 88 if not self.template.cache_enabled: 89 return creation_function() 90 91 return self.impl.get_or_create( 92 key, 93 creation_function, 94 **self._get_cache_kw(kw, context)) 95 96 def set(self, key, value, **kw): 97 """Place a value in the cache. 98 99 :param key: the value's key. 100 :param value: the value. 101 :param \**kw: cache configuration arguments. 102 103 """ 104 105 self.impl.set(key, value, **self._get_cache_kw(kw, None)) 106 107 put = set 108 """A synonym for :meth:`.Cache.set`. 109 110 This is here for backwards compatibility. 111 112 """ 113 114 def get(self, key, **kw): 115 """Retrieve a value from the cache. 116 117 :param key: the value's key. 118 :param \**kw: cache configuration arguments. The 119 backend is configured using these arguments upon first request. 120 Subsequent requests that use the same series of configuration 121 values will use that same backend. 122 123 """ 124 return self.impl.get(key, **self._get_cache_kw(kw, None)) 125 126 def invalidate(self, key, **kw): 127 """Invalidate a value in the cache. 128 129 :param key: the value's key. 130 :param \**kw: cache configuration arguments. The 131 backend is configured using these arguments upon first request. 132 Subsequent requests that use the same series of configuration 133 values will use that same backend. 134 135 """ 136 self.impl.invalidate(key, **self._get_cache_kw(kw, None)) 137 138 def invalidate_body(self): 139 """Invalidate the cached content of the "body" method for this 140 template. 141 142 """ 143 self.invalidate('render_body', __M_defname='render_body') 144 145 def invalidate_def(self, name): 146 """Invalidate the cached content of a particular ``<%def>`` within this 147 template. 148 149 """ 150 151 self.invalidate('render_%s' % name, __M_defname='render_%s' % name) 152 153 def invalidate_closure(self, name): 154 """Invalidate a nested ``<%def>`` within this template. 155 156 Caching of nested defs is a blunt tool as there is no 157 management of scope -- nested defs that use cache tags 158 need to have names unique of all other nested defs in the 159 template, else their content will be overwritten by 160 each other. 161 162 """ 163 164 self.invalidate(name, __M_defname=name) 165 166 def _get_cache_kw(self, kw, context): 167 defname = kw.pop('__M_defname', None) 168 if not defname: 169 tmpl_kw = self.template.cache_args.copy() 170 tmpl_kw.update(kw) 171 elif defname in self._def_regions: 172 tmpl_kw = self._def_regions[defname] 173 else: 174 tmpl_kw = self.template.cache_args.copy() 175 tmpl_kw.update(kw) 176 self._def_regions[defname] = tmpl_kw 177 if context and self.impl.pass_context: 178 tmpl_kw = tmpl_kw.copy() 179 tmpl_kw.setdefault('context', context) 180 return tmpl_kw 181 182 183 class CacheImpl(object): 184 """Provide a cache implementation for use by :class:`.Cache`.""" 185 186 def __init__(self, cache): 187 self.cache = cache 188 189 pass_context = False 190 """If ``True``, the :class:`.Context` will be passed to 191 :meth:`get_or_create <.CacheImpl.get_or_create>` as the name ``'context'``. 192 """ 193 194 def get_or_create(self, key, creation_function, **kw): 195 """Retrieve a value from the cache, using the given creation function 196 to generate a new value. 197 198 This function *must* return a value, either from 199 the cache, or via the given creation function. 200 If the creation function is called, the newly 201 created value should be populated into the cache 202 under the given key before being returned. 203 204 :param key: the value's key. 205 :param creation_function: function that when called generates 206 a new value. 207 :param \**kw: cache configuration arguments. 208 209 """ 210 raise NotImplementedError() 211 212 def set(self, key, value, **kw): 213 """Place a value in the cache. 214 215 :param key: the value's key. 216 :param value: the value. 217 :param \**kw: cache configuration arguments. 218 219 """ 220 raise NotImplementedError() 221 222 def get(self, key, **kw): 223 """Retrieve a value from the cache. 224 225 :param key: the value's key. 226 :param \**kw: cache configuration arguments. 227 228 """ 229 raise NotImplementedError() 230 231 def invalidate(self, key, **kw): 232 """Invalidate a value in the cache. 233 234 :param key: the value's key. 235 :param \**kw: cache configuration arguments. 236 237 """ 238 raise NotImplementedError() 239