Anonymous avatar Anonymous committed ecc26c1

dev...

Comments (0)

Files changed (8)

 dogpile.cache
 =============
 
-Provides a simple caching pattern to use with the `dogpile <http://pypi.python.org/pypi/dogpile>`_. locking system,
-including rudimentary backends.  It effectively completes the replacement
-of Beaker as far as caching is concerned, providing an open-ended and simple
-pattern to configure caching.  New backends are very easy to create and use;
-users are encouraged to adapt the provided backends for their own needs, as 
-high volume caching requires lots of tweaks and adjustments specific to
-an application and its environment.
+Provides a simple caching pattern to use with the `dogpile <http://pypi.python.org/pypi/dogpile>`_
+ locking system, including rudimentary backends. It effectively completes the
+replacement of Beaker as far as caching is concerned, providing an open-ended
+and simple pattern to configure caching. New backends are very easy to create
+and use; users are encouraged to adapt the provided backends for their own
+needs, as high volume caching requires lots of tweaks and adjustments specific
+to an application and its environment.
 
 Usage
 -----
   new values to be placed in the cache.
 
 The most common caching style in use these days is via memcached, so an example
-of this using the pylibmc backend looks like::
+of this using the `pylibmc <http://pypi.python.org/pypi/pylibmc>`_ backend looks like::
 
     from dogpile.cache import make_region
 
 
     data = region.put("somekey", "somevalue")
 
+The values we receive for the backend here are instances of
+``CachedValue``.  This is a tuple subclass of length two, of the form::
+
+    (payload, metadata)
+
+Where "payload" is the thing being cached, and "metadata" is information
+we store in the cache regarding the created time, expiration time.  The
+tuple can be pickled assuming the payload is pickleable, or to get a serialized
+version of just the metadata, you can call the ``serialized_metadata`` accessor,
+which is then turned back into a ``CachedValue`` via the ``deserialize``
+method::
+
+    from dogpile.cache.backend import CachedValue
+
+    def get(self, key):
+        my_cached_value = my_cache.get(key)
+        serialized_metadata, payload = my_cached_value.split("|")
+        return CachedValue.deserialize(payload, serialized_metadata)
+
+    def put(self, key, value):
+        my_value_to_cache = value.serialized_metadata + "|" + repr(value.payload)
+        my_cache.put(key, my_value_to_cache)
+
+
 Region Arguments
 ----------------
 

dogpile/cache/backends/__init__.py

+from dogpile.cache.region import register_backend
+
+register_backend("dbm", "dogpile.cache.backends.dbm", "DbmBackend")
+register_backend("pylibmc", "dogpile.cache.backends.memcached", "PylibmcBackend")
Add a comment to this file

dogpile/cache/backends/dbm.py

Empty file added.

Add a comment to this file

dogpile/cache/backends/memcached.py

Empty file added.

dogpile/cache/plugins/mako.py

+from mako.cache import CacheImpl
+
+class MakoPlugin(CacheImpl):
+    def __init__(self, cache):
+        super(MakoPlugin, self).__init__(cache)
+        try:
+            self.regions = self.cache.template.cache_args['regions']
+        except KeyError:
+            raise KeyError(
+                "'cache_regions' argument is required on the "
+                "Mako Lookup or Template object for usage "
+                "with the dogpile.cache plugin.")
+
+    def _get_region(self, **kw):
+        try:
+            region = kw['region']
+        except KeyError:
+            raise KeyError(
+                "'cache_region' argument must be specified with 'cache=True'"
+                "within templates for usage with the dogpile.cache plugin.")
+        try:
+            return self.regions[region]
+        except KeyError:
+            raise KeyError("No such region '%s'" % region)
+
+    def get_and_replace(self, key, creation_function, **kw):
+        return self._get_region(**kw).get_or_create(key, creation_function)
+
+    def put(self, key, value, **kw):
+        self._get_region(**kw).put(key, value)
+ 
+    def get(self, key, **kw):
+        return self._get_region(**kw).get(key)
+ 
+    def invalidate(self, key, **kw):
+        self._get_region(**kw).delete(key)

dogpile/cache/region.py

+import inspect
+
+
+_backend_loader = PluginLoader("dogpile.cache")
+register_backend = _backend_loader.register
+import backends
+
+class CacheRegion(object):
+    """A front end to a particular cache backend."""
+
+    def __init__(self, name, 
+            expiration_time=None,
+            arguments=None,
+            function_key_generator=_function_key_generator
+            key_mangler=None,
+        ):
+        self.backend = _backend_loadeer.load(name)(arguents)
+        self.function_key_generator = function_key_genertor
+        self.key_mangler = key_mangler
+        self.dogpile = Dogpile(expiration_time, init=True)
+
+    def get(self, key):
+        """Return a value from the cache, based on the given key.
+
+        While it's typical the key is a string, it's passed through to the
+        underlying backend so can be of any type recognized by the backend. If
+        the value is not present, returns the token ``NO_VALUE``. ``NO_VALUE``
+        evaluates to False, but is separate from ``None`` to distinguish
+        between a cached value of ``None``. Note that the ``expiration_time``
+        argument is **not** used here - this method is a direct line to the
+        backend's behavior. 
+        """
+
+        if self.key_mangler:
+            key = self.key_mangler(key)
+        value = self.backend.get(key)
+        return value.payload
+
+    def get_or_create(self, key, creator):
+        """Similar to ``get``, will use the given "creation" function to create a new
+        value if the value does not exist.
+
+        This will use the underlying dogpile/
+        expiration mechanism to determine when/how the creation function is called.
+
+        """
+        dogpile = 
+        with dogpile.acquire(gen_cached, get_value) as value:
+            return value
+
+    def _dogpile_get_value(self):
+        value = 
+             with mc_pool.reserve() as mc:
+                value = mc.get(key)
+                if value is None:
+                    raise NeedRegenerationException()
+                return value
+
+    import pylibmc
+    mc_pool = pylibmc.ThreadMappedPool(pylibmc.Client("localhost"))
+
+    from dogpile import Dogpile, NeedRegenerationException
+
+    def cached(key, expiration_time):
+        """A decorator that will cache the return value of a function
+        in memcached given a key."""
+
+        def get_value():
+             with mc_pool.reserve() as mc:
+                value = mc.get(key)
+                if value is None:
+                    raise NeedRegenerationException()
+                return value
+
+        dogpile = Dogpile(expiration_time, init=True)
+
+        def decorate(fn):
+            def gen_cached():
+                value = fn()
+                with mc_pool.reserve() as mc:
+                    mc.put(key, value)
+                return value
+
+            def invoke():
+                with dogpile.acquire(gen_cached, get_value) as value:
+                    return value
+            return invoke
+
+        return decorate
+
+
+
+    def put(self, key, value):
+        """Place a new value in the cache under the given key."""
+
+        if self.key_mangler:
+            key = self.key_mangler(key)
+        self.backend.put(key, CachedValue(value))
+
+
+    def delete(self, key):
+        """Remove a value from the cache.
+
+        This operation is idempotent (can be called multiple times, or on a 
+        non-existent key, safely)
+        """
+
+        if self.key_mangler:
+            key = self.key_mangler(key)
+
+        self.backend.delete(key)
+
+    def cache_on_arguments(self, fn):
+        """A function decorator that will cache the return value of the
+        function using a key derived from the name of the function, its
+        location within the application (i.e. source filename) as well as the
+        arguments passed to the function.
+
+         The generation of the key from the function is the big controversial
+        thing that was a source of user issues with Beaker. Dogpile provides
+        the latest and greatest algorithm used by Beaker, but also allows you
+        to use whatever function you want, by specifying it to
+        ``make_region()`` using the ``function_key_generator`` argument. 
+        """
+
+
+make_region = CacheRegion
+
+

dogpile/cache/util.py

+import sha1
+import inspect
+
+class PluginLoader(object):
+    def __init__(self, group):
+        self.group = group
+        self.impls = {}
+
+    def load(self, name):
+        if name in self.impls:
+             return self.impls[name]()
+        else:
+            import pkg_resources
+            for impl in pkg_resources.iter_entry_points(
+                                self.group, 
+                                name):
+                self.impls[name] = impl.load
+                return impl.load()
+            else:
+                raise exceptions.RuntimeException(
+                        "Can't load plugin %s %s" % 
+                        (self.group, name))
+
+    def register(self, name, modulepath, objname):
+        def load():
+            mod = __import__(modulepath)
+            for token in modulepath.split(".")[1:]:
+                mod = getattr(mod, token)
+            return getattr(mod, objname)
+        self.impls[name] = load
+
+
+def _function_key_generator(fn):
+    """Return a function that generates a string
+    key, based on a given function as well as
+    arguments to the returned function itself.
+    
+    This is used by :meth:`.CacheRegion.cache_on_arguments`
+    to generate a cache key from a decorated function.
+    
+    It can be replaced using the ``function_key_generator``
+    argument passed to :func:`.make_region`.
+    
+    """
+
+    kls = None
+    if hasattr(fn, 'im_func'):
+        kls = fn.im_class
+        fn = fn.im_func
+
+    if kls:
+        namespace = '%s.%s' % (kls.__module__, kls.__name__)
+    else:
+        namespace = '%s|%s' % (inspect.getsourcefile(func), func.__name__)
+
+    args = inspect.getargspec(func)
+    has_self = args[0] and args[0][0] in ('self', 'cls')
+    def generate_key(*args, **kw):
+        if kw:
+            raise ValueError(
+                    "dogpile.cache's default key creation "
+                    "function does not accept keyword arguments.")
+        if has_self:
+            args = args[1:]
+        return  " ".join(map(unicode, deco_args + args))
+    return generate_key
+
+def _sha1_mangle_key(key):
+    """Default key mangler"""
+
+    return sha1(key).hexdigest()
       namespace_packages=['dogpile'],
       entry_points="""
       [mako.cache]
-      dogpile = dogpile.cache.plugins.mako_plugin:MakoPlugin
+      dogpile = dogpile.cache.plugins.mako:MakoPlugin
       """
       zip_safe=False,
       install_requires=['dogpile>=0.1.0'],
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.