1# mako/cache.py 2# Copyright 2006-2023 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 7from mako import util 8 9_cache_plugins = util.PluginLoader("mako.cache") 10 11register_plugin = _cache_plugins.register 12register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl") 13 14 15class Cache: 16 17 """Represents a data content cache made available to the module 18 space of a specific :class:`.Template` object. 19 20 .. versionadded:: 0.6 21 :class:`.Cache` by itself is mostly a 22 container for a :class:`.CacheImpl` object, which implements 23 a fixed API to provide caching services; specific subclasses exist to 24 implement different 25 caching strategies. Mako includes a backend that works with 26 the Beaker caching system. Beaker itself then supports 27 a number of backends (i.e. file, memory, memcached, etc.) 28 29 The construction of a :class:`.Cache` is part of the mechanics 30 of a :class:`.Template`, and programmatic access to this 31 cache is typically via the :attr:`.Template.cache` attribute. 32 33 """ 34 35 impl = None 36 """Provide the :class:`.CacheImpl` in use by this :class:`.Cache`. 37 38 This accessor allows a :class:`.CacheImpl` with additional 39 methods beyond that of :class:`.Cache` to be used programmatically. 40 41 """ 42 43 id = None 44 """Return the 'id' that identifies this cache. 45 46 This is a value that should be globally unique to the 47 :class:`.Template` associated with this cache, and can 48 be used by a caching system to name a local container 49 for data specific to this template. 50 51 """ 52 53 starttime = None 54 """Epochal time value for when the owning :class:`.Template` was 55 first compiled. 56 57 A cache implementation may wish to invalidate data earlier than 58 this timestamp; this has the effect of the cache for a specific 59 :class:`.Template` starting clean any time the :class:`.Template` 60 is recompiled, such as when the original template file changed on 61 the filesystem. 62 63 """ 64 65 def __init__(self, template, *args): 66 # check for a stale template calling the 67 # constructor 68 if isinstance(template, str) and args: 69 return 70 self.template = template 71 self.id = template.module.__name__ 72 self.starttime = template.module._modified_time 73 self._def_regions = {} 74 self.impl = self._load_impl(self.template.cache_impl) 75 76 def _load_impl(self, name): 77 return _cache_plugins.load(name)(self) 78 79 def get_or_create(self, key, creation_function, **kw): 80 """Retrieve a value from the cache, using the given creation function 81 to generate a new value.""" 82 83 return self._ctx_get_or_create(key, creation_function, None, **kw) 84 85 def _ctx_get_or_create(self, key, creation_function, context, **kw): 86 """Retrieve a value from the cache, using the given creation function 87 to generate a new value.""" 88 89 if not self.template.cache_enabled: 90 return creation_function() 91 92 return self.impl.get_or_create( 93 key, creation_function, **self._get_cache_kw(kw, context) 94 ) 95 96 def set(self, key, value, **kw): 97 r"""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 r"""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 r"""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 183class CacheImpl: 184 185 """Provide a cache implementation for use by :class:`.Cache`.""" 186 187 def __init__(self, cache): 188 self.cache = cache 189 190 pass_context = False 191 """If ``True``, the :class:`.Context` will be passed to 192 :meth:`get_or_create <.CacheImpl.get_or_create>` as the name ``'context'``. 193 """ 194 195 def get_or_create(self, key, creation_function, **kw): 196 r"""Retrieve a value from the cache, using the given creation function 197 to generate a new value. 198 199 This function *must* return a value, either from 200 the cache, or via the given creation function. 201 If the creation function is called, the newly 202 created value should be populated into the cache 203 under the given key before being returned. 204 205 :param key: the value's key. 206 :param creation_function: function that when called generates 207 a new value. 208 :param \**kw: cache configuration arguments. 209 210 """ 211 raise NotImplementedError() 212 213 def set(self, key, value, **kw): 214 r"""Place a value in the cache. 215 216 :param key: the value's key. 217 :param value: the value. 218 :param \**kw: cache configuration arguments. 219 220 """ 221 raise NotImplementedError() 222 223 def get(self, key, **kw): 224 r"""Retrieve a value from the cache. 225 226 :param key: the value's key. 227 :param \**kw: cache configuration arguments. 228 229 """ 230 raise NotImplementedError() 231 232 def invalidate(self, key, **kw): 233 r"""Invalidate a value in the cache. 234 235 :param key: the value's key. 236 :param \**kw: cache configuration arguments. 237 238 """ 239 raise NotImplementedError() 240