summaryrefslogtreecommitdiffstats
path: root/module/lib/beaker/cache.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/lib/beaker/cache.py')
-rw-r--r--module/lib/beaker/cache.py589
1 files changed, 0 insertions, 589 deletions
diff --git a/module/lib/beaker/cache.py b/module/lib/beaker/cache.py
deleted file mode 100644
index 0ae96e020..000000000
--- a/module/lib/beaker/cache.py
+++ /dev/null
@@ -1,589 +0,0 @@
-"""This package contains the "front end" classes and functions
-for Beaker caching.
-
-Included are the :class:`.Cache` and :class:`.CacheManager` classes,
-as well as the function decorators :func:`.region_decorate`,
-:func:`.region_invalidate`.
-
-"""
-import warnings
-
-import beaker.container as container
-import beaker.util as util
-from beaker.crypto.util import sha1
-from beaker.exceptions import BeakerException, InvalidCacheBackendError
-from beaker.synchronization import _threading
-
-import beaker.ext.memcached as memcached
-import beaker.ext.database as database
-import beaker.ext.sqla as sqla
-import beaker.ext.google as google
-
-# Initialize the cache region dict
-cache_regions = {}
-"""Dictionary of 'region' arguments.
-
-A "region" is a string name that refers to a series of cache
-configuration arguments. An application may have multiple
-"regions" - one which stores things in a memory cache, one
-which writes data to files, etc.
-
-The dictionary stores string key names mapped to dictionaries
-of configuration arguments. Example::
-
- from beaker.cache import cache_regions
- cache_regions.update({
- 'short_term':{
- 'expire':'60',
- 'type':'memory'
- },
- 'long_term':{
- 'expire':'1800',
- 'type':'dbm',
- 'data_dir':'/tmp',
- }
- })
-"""
-
-
-cache_managers = {}
-
-
-class _backends(object):
- initialized = False
-
- def __init__(self, clsmap):
- self._clsmap = clsmap
- self._mutex = _threading.Lock()
-
- def __getitem__(self, key):
- try:
- return self._clsmap[key]
- except KeyError, e:
- if not self.initialized:
- self._mutex.acquire()
- try:
- if not self.initialized:
- self._init()
- self.initialized = True
-
- return self._clsmap[key]
- finally:
- self._mutex.release()
-
- raise e
-
- def _init(self):
- try:
- import pkg_resources
-
- # Load up the additional entry point defined backends
- for entry_point in pkg_resources.iter_entry_points('beaker.backends'):
- try:
- namespace_manager = entry_point.load()
- name = entry_point.name
- if name in self._clsmap:
- raise BeakerException("NamespaceManager name conflict,'%s' "
- "already loaded" % name)
- self._clsmap[name] = namespace_manager
- except (InvalidCacheBackendError, SyntaxError):
- # Ignore invalid backends
- pass
- except:
- import sys
- from pkg_resources import DistributionNotFound
- # Warn when there's a problem loading a NamespaceManager
- if not isinstance(sys.exc_info()[1], DistributionNotFound):
- import traceback
- from StringIO import StringIO
- tb = StringIO()
- traceback.print_exc(file=tb)
- warnings.warn(
- "Unable to load NamespaceManager "
- "entry point: '%s': %s" % (
- entry_point,
- tb.getvalue()),
- RuntimeWarning, 2)
- except ImportError:
- pass
-
-# Initialize the basic available backends
-clsmap = _backends({
- 'memory': container.MemoryNamespaceManager,
- 'dbm': container.DBMNamespaceManager,
- 'file': container.FileNamespaceManager,
- 'ext:memcached': memcached.MemcachedNamespaceManager,
- 'ext:database': database.DatabaseNamespaceManager,
- 'ext:sqla': sqla.SqlaNamespaceManager,
- 'ext:google': google.GoogleNamespaceManager,
- })
-
-
-def cache_region(region, *args):
- """Decorate a function such that its return result is cached,
- using a "region" to indicate the cache arguments.
-
- Example::
-
- from beaker.cache import cache_regions, cache_region
-
- # configure regions
- cache_regions.update({
- 'short_term':{
- 'expire':'60',
- 'type':'memory'
- }
- })
-
- @cache_region('short_term', 'load_things')
- def load(search_term, limit, offset):
- '''Load from a database given a search term, limit, offset.'''
- return database.query(search_term)[offset:offset + limit]
-
- The decorator can also be used with object methods. The ``self``
- argument is not part of the cache key. This is based on the
- actual string name ``self`` being in the first argument
- position (new in 1.6)::
-
- class MyThing(object):
- @cache_region('short_term', 'load_things')
- def load(self, search_term, limit, offset):
- '''Load from a database given a search term, limit, offset.'''
- return database.query(search_term)[offset:offset + limit]
-
- Classmethods work as well - use ``cls`` as the name of the class argument,
- and place the decorator around the function underneath ``@classmethod``
- (new in 1.6)::
-
- class MyThing(object):
- @classmethod
- @cache_region('short_term', 'load_things')
- def load(cls, search_term, limit, offset):
- '''Load from a database given a search term, limit, offset.'''
- return database.query(search_term)[offset:offset + limit]
-
- :param region: String name of the region corresponding to the desired
- caching arguments, established in :attr:`.cache_regions`.
-
- :param \*args: Optional ``str()``-compatible arguments which will uniquely
- identify the key used by this decorated function, in addition
- to the positional arguments passed to the function itself at call time.
- This is recommended as it is needed to distinguish between any two functions
- or methods that have the same name (regardless of parent class or not).
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments, and the arguments must support
- being stringified with ``str()``. The concatenation
- of the ``str()`` version of each argument, combined
- with that of the ``*args`` sent to the decorator,
- forms the unique cache key.
-
- .. note::
-
- When a method on a class is decorated, the ``self`` or ``cls``
- argument in the first position is
- not included in the "key" used for caching. New in 1.6.
-
- """
- return _cache_decorate(args, None, None, region)
-
-
-def region_invalidate(namespace, region, *args):
- """Invalidate a cache region corresponding to a function
- decorated with :func:`.cache_region`.
-
- :param namespace: The namespace of the cache to invalidate. This is typically
- a reference to the original function (as returned by the :func:`.cache_region`
- decorator), where the :func:`.cache_region` decorator applies a "memo" to
- the function in order to locate the string name of the namespace.
-
- :param region: String name of the region used with the decorator. This can be
- ``None`` in the usual case that the decorated function itself is passed,
- not the string name of the namespace.
-
- :param args: Stringifyable arguments that are used to locate the correct
- key. This consists of the ``*args`` sent to the :func:`.cache_region`
- decorator itself, plus the ``*args`` sent to the function itself
- at runtime.
-
- Example::
-
- from beaker.cache import cache_regions, cache_region, region_invalidate
-
- # configure regions
- cache_regions.update({
- 'short_term':{
- 'expire':'60',
- 'type':'memory'
- }
- })
-
- @cache_region('short_term', 'load_data')
- def load(search_term, limit, offset):
- '''Load from a database given a search term, limit, offset.'''
- return database.query(search_term)[offset:offset + limit]
-
- def invalidate_search(search_term, limit, offset):
- '''Invalidate the cached storage for a given search term, limit, offset.'''
- region_invalidate(load, 'short_term', 'load_data', search_term, limit, offset)
-
- Note that when a method on a class is decorated, the first argument ``cls``
- or ``self`` is not included in the cache key. This means you don't send
- it to :func:`.region_invalidate`::
-
- class MyThing(object):
- @cache_region('short_term', 'some_data')
- def load(self, search_term, limit, offset):
- '''Load from a database given a search term, limit, offset.'''
- return database.query(search_term)[offset:offset + limit]
-
- def invalidate_search(self, search_term, limit, offset):
- '''Invalidate the cached storage for a given search term, limit, offset.'''
- region_invalidate(self.load, 'short_term', 'some_data', search_term, limit, offset)
-
- """
- if callable(namespace):
- if not region:
- region = namespace._arg_region
- namespace = namespace._arg_namespace
-
- if not region:
- raise BeakerException("Region or callable function "
- "namespace is required")
- else:
- region = cache_regions[region]
-
- cache = Cache._get_cache(namespace, region)
- _cache_decorator_invalidate(cache, region['key_length'], args)
-
-
-class Cache(object):
- """Front-end to the containment API implementing a data cache.
-
- :param namespace: the namespace of this Cache
-
- :param type: type of cache to use
-
- :param expire: seconds to keep cached data
-
- :param expiretime: seconds to keep cached data (legacy support)
-
- :param starttime: time when cache was cache was
-
- """
- def __init__(self, namespace, type='memory', expiretime=None,
- starttime=None, expire=None, **nsargs):
- try:
- cls = clsmap[type]
- if isinstance(cls, InvalidCacheBackendError):
- raise cls
- except KeyError:
- raise TypeError("Unknown cache implementation %r" % type)
- self.namespace_name = namespace
- self.namespace = cls(namespace, **nsargs)
- self.expiretime = expiretime or expire
- self.starttime = starttime
- self.nsargs = nsargs
-
- @classmethod
- def _get_cache(cls, namespace, kw):
- key = namespace + str(kw)
- try:
- return cache_managers[key]
- except KeyError:
- cache_managers[key] = cache = cls(namespace, **kw)
- return cache
-
- def put(self, key, value, **kw):
- self._get_value(key, **kw).set_value(value)
- set_value = put
-
- def get(self, key, **kw):
- """Retrieve a cached value from the container"""
- return self._get_value(key, **kw).get_value()
- get_value = get
-
- def remove_value(self, key, **kw):
- mycontainer = self._get_value(key, **kw)
- mycontainer.clear_value()
- remove = remove_value
-
- def _get_value(self, key, **kw):
- if isinstance(key, unicode):
- key = key.encode('ascii', 'backslashreplace')
-
- if 'type' in kw:
- return self._legacy_get_value(key, **kw)
-
- kw.setdefault('expiretime', self.expiretime)
- kw.setdefault('starttime', self.starttime)
-
- return container.Value(key, self.namespace, **kw)
-
- @util.deprecated("Specifying a "
- "'type' and other namespace configuration with cache.get()/put()/etc. "
- "is deprecated. Specify 'type' and other namespace configuration to "
- "cache_manager.get_cache() and/or the Cache constructor instead.")
- def _legacy_get_value(self, key, type, **kw):
- expiretime = kw.pop('expiretime', self.expiretime)
- starttime = kw.pop('starttime', None)
- createfunc = kw.pop('createfunc', None)
- kwargs = self.nsargs.copy()
- kwargs.update(kw)
- c = Cache(self.namespace.namespace, type=type, **kwargs)
- return c._get_value(key, expiretime=expiretime, createfunc=createfunc,
- starttime=starttime)
-
- def clear(self):
- """Clear all the values from the namespace"""
- self.namespace.remove()
-
- # dict interface
- def __getitem__(self, key):
- return self.get(key)
-
- def __contains__(self, key):
- return self._get_value(key).has_current_value()
-
- def has_key(self, key):
- return key in self
-
- def __delitem__(self, key):
- self.remove_value(key)
-
- def __setitem__(self, key, value):
- self.put(key, value)
-
-
-class CacheManager(object):
- def __init__(self, **kwargs):
- """Initialize a CacheManager object with a set of options
-
- Options should be parsed with the
- :func:`~beaker.util.parse_cache_config_options` function to
- ensure only valid options are used.
-
- """
- self.kwargs = kwargs
- self.regions = kwargs.pop('cache_regions', {})
-
- # Add these regions to the module global
- cache_regions.update(self.regions)
-
- def get_cache(self, name, **kwargs):
- kw = self.kwargs.copy()
- kw.update(kwargs)
- return Cache._get_cache(name, kw)
-
- def get_cache_region(self, name, region):
- if region not in self.regions:
- raise BeakerException('Cache region not configured: %s' % region)
- kw = self.regions[region]
- return Cache._get_cache(name, kw)
-
- def region(self, region, *args):
- """Decorate a function to cache itself using a cache region
-
- The region decorator requires arguments if there are more than
- two of the same named function, in the same module. This is
- because the namespace used for the functions cache is based on
- the functions name and the module.
-
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things():
-
- @cache.region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- return cache_region(region, *args)
-
- def region_invalidate(self, namespace, region, *args):
- """Invalidate a cache region namespace or decorated function
-
- This function only invalidates cache spaces created with the
- cache_region decorator.
-
- :param namespace: Either the namespace of the result to invalidate, or the
- cached function
-
- :param region: The region the function was cached to. If the function was
- cached to a single region then this argument can be None
-
- :param args: Arguments that were used to differentiate the cached
- function as well as the arguments passed to the decorated
- function
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
- def populate_things(invalidate=False):
-
- @cache.region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- cache.region_invalidate(load, None, 'some_data',
- 'rabbits', 20, 0)
- return load('rabbits', 20, 0)
-
-
- """
- return region_invalidate(namespace, region, *args)
-
- def cache(self, *args, **kwargs):
- """Decorate a function to cache itself with supplied parameters
-
- :param args: Used to make the key unique for this function, as in region()
- above.
-
- :param kwargs: Parameters to be passed to get_cache(), will override defaults
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things():
-
- @cache.cache('mycache', expire=15)
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- return _cache_decorate(args, self, kwargs, None)
-
- def invalidate(self, func, *args, **kwargs):
- """Invalidate a cache decorated function
-
- This function only invalidates cache spaces created with the
- cache decorator.
-
- :param func: Decorated function to invalidate
-
- :param args: Used to make the key unique for this function, as in region()
- above.
-
- :param kwargs: Parameters that were passed for use by get_cache(), note that
- this is only required if a ``type`` was specified for the
- function
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things(invalidate=False):
-
- @cache.cache('mycache', type="file", expire=15)
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- cache.invalidate(load, 'mycache', 'rabbits', 20, 0, type="file")
- return load('rabbits', 20, 0)
-
- """
- namespace = func._arg_namespace
-
- cache = self.get_cache(namespace, **kwargs)
- if hasattr(func, '_arg_region'):
- key_length = cache_regions[func._arg_region]['key_length']
- else:
- key_length = kwargs.pop('key_length', 250)
- _cache_decorator_invalidate(cache, key_length, args)
-
-
-def _cache_decorate(deco_args, manager, kwargs, region):
- """Return a caching function decorator."""
-
- cache = [None]
-
- def decorate(func):
- namespace = util.func_namespace(func)
- skip_self = util.has_self_arg(func)
-
- def cached(*args):
- if not cache[0]:
- if region is not None:
- if region not in cache_regions:
- raise BeakerException(
- 'Cache region not configured: %s' % region)
- reg = cache_regions[region]
- if not reg.get('enabled', True):
- return func(*args)
- cache[0] = Cache._get_cache(namespace, reg)
- elif manager:
- cache[0] = manager.get_cache(namespace, **kwargs)
- else:
- raise Exception("'manager + kwargs' or 'region' "
- "argument is required")
-
- if skip_self:
- try:
- cache_key = " ".join(map(str, deco_args + args[1:]))
- except UnicodeEncodeError:
- cache_key = " ".join(map(unicode, deco_args + args[1:]))
- else:
- try:
- cache_key = " ".join(map(str, deco_args + args))
- except UnicodeEncodeError:
- cache_key = " ".join(map(unicode, deco_args + args))
- if region:
- key_length = cache_regions[region]['key_length']
- else:
- key_length = kwargs.pop('key_length', 250)
- if len(cache_key) + len(namespace) > int(key_length):
- cache_key = sha1(cache_key).hexdigest()
-
- def go():
- return func(*args)
-
- return cache[0].get_value(cache_key, createfunc=go)
- cached._arg_namespace = namespace
- if region is not None:
- cached._arg_region = region
- return cached
- return decorate
-
-
-def _cache_decorator_invalidate(cache, key_length, args):
- """Invalidate a cache key based on function arguments."""
-
- try:
- cache_key = " ".join(map(str, args))
- except UnicodeEncodeError:
- cache_key = " ".join(map(unicode, args))
- if len(cache_key) + len(cache.namespace_name) > key_length:
- cache_key = sha1(cache_key).hexdigest()
- cache.remove_value(cache_key)