Commits

Anonymous committed 4192624

dev

Comments (0)

Files changed (3)

dogpile/cache/api.py

+import operator
+
+class NoValue(object):
+    """Describe a missing cache value.
+    
+    The :attr:`.NO_VALUE` module global
+    should be used.
+    
+    """
+    @property
+    def payload(self):
+        return self
+
+    def __nonzero__(self):
+        return False
+
+NO_VALUE = NoValue()
+"""Value returned from ``get()`` that describes 
+a  key not present."""
+
+class CachedValue(tuple):
+    """Represent a value stored in the cache.
+    
+    :class:.`CachedValue` is a two-tuple of
+    ``(payload, metadata)``, where ``metadata``
+    is dogpile.cache's tracking information (
+    currently the creation time).  The metadata
+    and tuple structure is pickleable, if 
+    the backend requires serialization.
+    
+    """
+    payload = operator.itemgetter(0)
+    metadata = operator.itemgetter(1)
+
+    def __new__(cls, payload, metadata):
+        return tuple.__new__(cls, (payload, metadata))
+
+class CacheBackend(object):
+    """Base class for backend implementations."""
+
+    def __init__(self, arguments):
+        """Construct a new :class:`.CacheBackend`.
+        
+        Subclasses should override this to
+        handle the given arguments.
+        
+        :param arguments: The ``arguments`` parameter
+         passed to :func:`.make_registry`.
+         
+        """
+        raise NotImplementedError()
+
+    def get(self, key):
+        """Retrieve a value from the cache.
+        
+        The returned value should be an instance of
+        :class:`.CachedValue`, or ``NO_VALUE`` if
+        not present.
+        
+        """
+        raise NotImplementedError()
+
+    def put(self, key, value):
+        """Put a value in the cache.
+        
+        The key will be whatever was passed
+        to the registry, processed by the
+        "key mangling" function, if any.
+        The value will always be an instance
+        of :class:`.CachedValue`.
+        
+        """
+        raise NotImplementedError()
+
+    def delete(self, key):
+        """Delete a value from the cache.
+        
+        The key will be whatever was passed
+        to the registry, processed by the
+        "key mangling" function, if any.
+        
+        The behavior here should be idempotent,
+        that is, can be called any number of times
+        regardless of whether or not the
+        key exists.
+        """
+        raise NotImplementedError()

dogpile/cache/region.py

-import inspect
 
+from dogpile.cache.util import function_key_generator, PluginLoader
 
 _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
+            function_key_generator=function_key_generator
             key_mangler=None,
         ):
-        self.backend = _backend_loadeer.load(name)(arguents)
-        self.function_key_generator = function_key_genertor
+        """Construct a new :class:`.CacheRegion`.
+        
+        :param name: Cache backend name.
+        :param expiration_time: Expiration time, in seconds
+        :param arguments: Argument structure passed to the 
+         backend.  Is typically a dict.
+        :function_key_generator: Key generator used by
+         :meth:`.CacheRegion.cache_on_arguments`.
+        :key_mangler: Function which will be used on all incoming
+         keys before passing to the backend.  Defaults to ``None``.
+         A simple key mangler is the SHA1 mangler function
+         found at :meth:`.sha1_mangle_key`.
+         
+        """
+        self.backend = _backend_loader.load(name)(arguments)
+        self.function_key_generator = function_key_generator
         self.key_mangler = key_mangler
-        self.dogpile = Dogpile(expiration_time, init=True)
+        self.dogpile_registry = Dogpile.registry(expiration_time)
 
     def get(self, key):
         """Return a value from the cache, based on the given key.
         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:
         expiration mechanism to determine when/how the creation function is called.
 
         """
-        dogpile = 
-        with dogpile.acquire(gen_cached, get_value) as value:
+        if self.key_mangler:
+            key = self.key_mangler(key)
+
+        def get_value():
+            value = self.backend.get(key)
+            if value is NO_VALUE:
+                raise NeedRegenerationException()
+            return value.payload, value.metadata["creation_time"]
+
+        def gen_value():
+            value = CachedValue(creator(), {"creation_time":time.time()})
+            self.backend.put(key, 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
-
-
+        dogpile = self.dogpile_registry.get(key)
+        with dogpile.acquire(gen_value, value_and_created_fn=get_value) as value:
+            return value
 
     def put(self, key, value):
         """Place a new value in the cache under the given key."""
         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.
+        
+        E.g.::
+        
+            @someregion.cache_on_arguments
+            def generate_something(x, y):
+                return somedatabase.query(x, y)
+                
+        The decorated function can then be called normally, where
+        data will be pulled from the cache region unless a new
+        value is needed::
+        
+            result = generate_something(5, 6)
+        
+        The function is also given an attribute ``invalidate``, which
+        provides for invalidation of the value.  Pass to ``invalidate()``
+        the same arguments you'd pass to the function itself to represent
+        a particular value::
+        
+            generate_something.invalidate(5, 6)
+        
+        The mechanism used to generate cache keys is controlled
+        by the ``function_key_generator`` function passed
+        to :class:`.CacheRegion`. It defaults to :func:`.function_key_generator`.    
 
-         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. 
         """
+        key_generator = self.function_key_generator(fn)
+        def decorate(*arg, **kw):
+            key = key_generator(*arg, **kw)
+            def creator():
+                return fn(*arg, **kw)
+            return self.get_or_create(key, creator)
 
+        def invalidate(*arg, **kw):
+            key = key_generator(*arg, **kw)
+            self.delete(key)
+
+        decorate.invalidate = invalidate
+
+        return decorate
 
 make_region = CacheRegion
 

dogpile/cache/util.py

         self.impls[name] = load
 
 
-def _function_key_generator(fn):
+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.
         return  " ".join(map(unicode, deco_args + args))
     return generate_key
 
-def _sha1_mangle_key(key):
-    """Default key mangler"""
+def sha1_mangle_key(key):
+    """a SHA1 key mangler."""
 
     return sha1(key).hexdigest()