| # mako/cache.py |
| # Copyright (C) 2006-2015 the Mako authors and contributors <see AUTHORS file> |
| # |
| # This module is part of Mako and is released under |
| # the MIT License: http://www.opensource.org/licenses/mit-license.php |
| |
| from mako import compat, util |
| |
| _cache_plugins = util.PluginLoader("mako.cache") |
| |
| register_plugin = _cache_plugins.register |
| register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl") |
| |
| |
| class Cache(object): |
| |
| """Represents a data content cache made available to the module |
| space of a specific :class:`.Template` object. |
| |
| .. versionadded:: 0.6 |
| :class:`.Cache` by itself is mostly a |
| container for a :class:`.CacheImpl` object, which implements |
| a fixed API to provide caching services; specific subclasses exist to |
| implement different |
| caching strategies. Mako includes a backend that works with |
| the Beaker caching system. Beaker itself then supports |
| a number of backends (i.e. file, memory, memcached, etc.) |
| |
| The construction of a :class:`.Cache` is part of the mechanics |
| of a :class:`.Template`, and programmatic access to this |
| cache is typically via the :attr:`.Template.cache` attribute. |
| |
| """ |
| |
| impl = None |
| """Provide the :class:`.CacheImpl` in use by this :class:`.Cache`. |
| |
| This accessor allows a :class:`.CacheImpl` with additional |
| methods beyond that of :class:`.Cache` to be used programmatically. |
| |
| """ |
| |
| id = None |
| """Return the 'id' that identifies this cache. |
| |
| This is a value that should be globally unique to the |
| :class:`.Template` associated with this cache, and can |
| be used by a caching system to name a local container |
| for data specific to this template. |
| |
| """ |
| |
| starttime = None |
| """Epochal time value for when the owning :class:`.Template` was |
| first compiled. |
| |
| A cache implementation may wish to invalidate data earlier than |
| this timestamp; this has the effect of the cache for a specific |
| :class:`.Template` starting clean any time the :class:`.Template` |
| is recompiled, such as when the original template file changed on |
| the filesystem. |
| |
| """ |
| |
| def __init__(self, template, *args): |
| # check for a stale template calling the |
| # constructor |
| if isinstance(template, compat.string_types) and args: |
| return |
| self.template = template |
| self.id = template.module.__name__ |
| self.starttime = template.module._modified_time |
| self._def_regions = {} |
| self.impl = self._load_impl(self.template.cache_impl) |
| |
| def _load_impl(self, name): |
| return _cache_plugins.load(name)(self) |
| |
| def get_or_create(self, key, creation_function, **kw): |
| """Retrieve a value from the cache, using the given creation function |
| to generate a new value.""" |
| |
| return self._ctx_get_or_create(key, creation_function, None, **kw) |
| |
| def _ctx_get_or_create(self, key, creation_function, context, **kw): |
| """Retrieve a value from the cache, using the given creation function |
| to generate a new value.""" |
| |
| if not self.template.cache_enabled: |
| return creation_function() |
| |
| return self.impl.get_or_create( |
| key, |
| creation_function, |
| **self._get_cache_kw(kw, context)) |
| |
| def set(self, key, value, **kw): |
| """Place a value in the cache. |
| |
| :param key: the value's key. |
| :param value: the value. |
| :param \**kw: cache configuration arguments. |
| |
| """ |
| |
| self.impl.set(key, value, **self._get_cache_kw(kw, None)) |
| |
| put = set |
| """A synonym for :meth:`.Cache.set`. |
| |
| This is here for backwards compatibility. |
| |
| """ |
| |
| def get(self, key, **kw): |
| """Retrieve a value from the cache. |
| |
| :param key: the value's key. |
| :param \**kw: cache configuration arguments. The |
| backend is configured using these arguments upon first request. |
| Subsequent requests that use the same series of configuration |
| values will use that same backend. |
| |
| """ |
| return self.impl.get(key, **self._get_cache_kw(kw, None)) |
| |
| def invalidate(self, key, **kw): |
| """Invalidate a value in the cache. |
| |
| :param key: the value's key. |
| :param \**kw: cache configuration arguments. The |
| backend is configured using these arguments upon first request. |
| Subsequent requests that use the same series of configuration |
| values will use that same backend. |
| |
| """ |
| self.impl.invalidate(key, **self._get_cache_kw(kw, None)) |
| |
| def invalidate_body(self): |
| """Invalidate the cached content of the "body" method for this |
| template. |
| |
| """ |
| self.invalidate('render_body', __M_defname='render_body') |
| |
| def invalidate_def(self, name): |
| """Invalidate the cached content of a particular ``<%def>`` within this |
| template. |
| |
| """ |
| |
| self.invalidate('render_%s' % name, __M_defname='render_%s' % name) |
| |
| def invalidate_closure(self, name): |
| """Invalidate a nested ``<%def>`` within this template. |
| |
| Caching of nested defs is a blunt tool as there is no |
| management of scope -- nested defs that use cache tags |
| need to have names unique of all other nested defs in the |
| template, else their content will be overwritten by |
| each other. |
| |
| """ |
| |
| self.invalidate(name, __M_defname=name) |
| |
| def _get_cache_kw(self, kw, context): |
| defname = kw.pop('__M_defname', None) |
| if not defname: |
| tmpl_kw = self.template.cache_args.copy() |
| tmpl_kw.update(kw) |
| elif defname in self._def_regions: |
| tmpl_kw = self._def_regions[defname] |
| else: |
| tmpl_kw = self.template.cache_args.copy() |
| tmpl_kw.update(kw) |
| self._def_regions[defname] = tmpl_kw |
| if context and self.impl.pass_context: |
| tmpl_kw = tmpl_kw.copy() |
| tmpl_kw.setdefault('context', context) |
| return tmpl_kw |
| |
| |
| class CacheImpl(object): |
| |
| """Provide a cache implementation for use by :class:`.Cache`.""" |
| |
| def __init__(self, cache): |
| self.cache = cache |
| |
| pass_context = False |
| """If ``True``, the :class:`.Context` will be passed to |
| :meth:`get_or_create <.CacheImpl.get_or_create>` as the name ``'context'``. |
| """ |
| |
| def get_or_create(self, key, creation_function, **kw): |
| """Retrieve a value from the cache, using the given creation function |
| to generate a new value. |
| |
| This function *must* return a value, either from |
| the cache, or via the given creation function. |
| If the creation function is called, the newly |
| created value should be populated into the cache |
| under the given key before being returned. |
| |
| :param key: the value's key. |
| :param creation_function: function that when called generates |
| a new value. |
| :param \**kw: cache configuration arguments. |
| |
| """ |
| raise NotImplementedError() |
| |
| def set(self, key, value, **kw): |
| """Place a value in the cache. |
| |
| :param key: the value's key. |
| :param value: the value. |
| :param \**kw: cache configuration arguments. |
| |
| """ |
| raise NotImplementedError() |
| |
| def get(self, key, **kw): |
| """Retrieve a value from the cache. |
| |
| :param key: the value's key. |
| :param \**kw: cache configuration arguments. |
| |
| """ |
| raise NotImplementedError() |
| |
| def invalidate(self, key, **kw): |
| """Invalidate a value in the cache. |
| |
| :param key: the value's key. |
| :param \**kw: cache configuration arguments. |
| |
| """ |
| raise NotImplementedError() |