diff options
Diffstat (limited to 'mako/cache.py')
| -rw-r--r-- | mako/cache.py | 233 |
1 files changed, 168 insertions, 65 deletions
diff --git a/mako/cache.py b/mako/cache.py index ce73ae5..5793987 100644 --- a/mako/cache.py +++ b/mako/cache.py @@ -6,89 +6,146 @@ from mako import exceptions -cache = None -class BeakerMissing(object): - def get_cache(self, name, **kwargs): - raise exceptions.RuntimeException("the Beaker package is required to use cache functionality.") +def register_plugin(name, modulename, attrname): + """Register the given :class:`.CacheImpl` under the given + name. + + This is an alternative to using a setuptools-installed entrypoint. + + """ + import pkg_resources + dist = pkg_resources.get_distribution("mako") + entry_map = dist.get_entry_map() + if 'mako.cache' not in entry_map: + entry_map['mako.cache'] = cache_map = {} + else: + cache_map = entry_map['mako.cache'] + cache_map[name] = \ + pkg_resources.EntryPoint.parse('%s = %s:%s' % (name, modulename, attrname), dist=dist) + +register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl") class Cache(object): """Represents a data content cache made available to the module - space of a :class:`.Template` object. - - :class:`.Cache` is a wrapper on top of a Beaker CacheManager object. - This object in turn references any number of "containers", each of - which defines its own backend (i.e. file, memory, memcached, etc.) - independently of the rest. + space of a specific :class:`.Template` object. + + As of Mako 0.5.1, :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. """ - - def __init__(self, id, starttime): - self.id = id - self.starttime = starttime - self.def_regions = {} - - def put(self, key, value, **kwargs): + + 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): + self.template = template + self.impl = self._load_impl(self.template.cache_impl) + self.id = template.module.__name__ + self.starttime = template.module._modified_time + self._def_regions = {} + + def _load_impl(self, name): + import pkg_resources + for impl in pkg_resources.iter_entry_points( + "mako.cache", + name): + return impl.load()(self) + else: + raise exceptions.RuntimeException( + "Cache implementation '%s' not present" % + name) + + def get_and_replace(self, key, creation_function, **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_and_replace(key, creation_function, **self._get_cache_kw(kw)) + + def put(self, key, value, **kw): """Place a value in the cache. :param key: the value's key. :param value: the value - :param \**kwargs: 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. + :param \**kw: cache configuration arguments. """ - - defname = kwargs.pop('defname', None) - expiretime = kwargs.pop('expiretime', None) - createfunc = kwargs.pop('createfunc', None) - - self._get_cache(defname, **kwargs).put_value(key, starttime=self.starttime, expiretime=expiretime) - - def get(self, key, **kwargs): + + self.impl.put(key, value, **self._get_cache_kw(kw)) + + def get(self, key, **kw): """Retrieve a value from the cache. :param key: the value's key. - :param \**kwargs: cache configuration arguments. The + :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)) - defname = kwargs.pop('defname', None) - expiretime = kwargs.pop('expiretime', None) - createfunc = kwargs.pop('createfunc', None) - - return self._get_cache(defname, **kwargs).get_value(key, starttime=self.starttime, expiretime=expiretime, createfunc=createfunc) - - def invalidate(self, key, **kwargs): + def invalidate(self, key, **kw): """Invalidate a value in the cache. :param key: the value's key. - :param \**kwargs: cache configuration arguments. The + :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. """ - defname = kwargs.pop('defname', None) - expiretime = kwargs.pop('expiretime', None) - createfunc = kwargs.pop('createfunc', None) - - self._get_cache(defname, **kwargs).remove_value(key, starttime=self.starttime, expiretime=expiretime) + self.impl.invalidate(key, **self._get_cache_kw(kw)) def invalidate_body(self): """Invalidate the cached content of the "body" method for this template. """ - self.invalidate('render_body', defname='render_body') + 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, defname='render_%s' % name) + self.invalidate('render_%s' % name, __M_defname='render_%s' % name) def invalidate_closure(self, name): """Invalidate a nested <%def> within this template. @@ -101,24 +158,70 @@ class Cache(object): """ - self.invalidate(name, defname=name) - - def _get_cache(self, defname, type=None, **kw): - global cache - if not cache: - try: - from beaker import cache as beaker_cache - cache = beaker_cache.CacheManager() - except ImportError: - # keep a fake cache around so subsequent - # calls don't attempt to re-import - cache = BeakerMissing() - - if type == 'memcached': - type = 'ext:memcached' - if not type: - (type, kw) = self.def_regions.get(defname, ('memory', {})) + self.invalidate(name, __M_defname=name) + + def _get_cache_kw(self, kw): + defname = kw.pop('__M_defname', None) + if not defname: + tmpl_kw = self.template.cache_args.copy() + tmpl_kw.update(kw) + return tmpl_kw + elif defname in self._def_regions: + return self._def_regions[defname] else: - self.def_regions[defname] = (type, kw) - return cache.get_cache(self.id, type=type, **kw) -
\ No newline at end of file + tmpl_kw = self.template.cache_args.copy() + tmpl_kw.update(kw) + self._def_regions[defname] = tmpl_kw + return tmpl_kw + +class CacheImpl(object): + """Provide a cache implementation for use by :class:`.Cache`.""" + + def __init__(self, cache): + self.cache = cache + + def get_and_replace(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 put(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() |
