Anonymous avatar Anonymous committed 178b7de

initial rev

Comments (0)

Files changed (10)

+syntax:regexp
+^build/
+^doc/build/output
+.pyc$
+.orig$
+.egg-info
+.*,cover
+.un~
+\.coverage
+\.DS_Store
+test.cfg
+Copyright (c) 2011 Mike Bayer
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author or contributors may not be used to endorse or
+   promote products derived from this software without specific prior
+   written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+# any kind of "*" pulls in __init__.pyc files,
+# so all extensions are explicit.
+
+recursive-include doc *.html *.css *.txt *.js *.jpg *.png *.py Makefile *.rst *.mako *.sty
+recursive-include examples *.py
+recursive-include tests *.py *.dat
+
+include README* LICENSE distribute_setup.py ez_setup.py CHANGES*
+prune doc/build/output
+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.
+
+Usage
+-----
+
+A dogpile.cache configuration consists of the following components:
+
+* A *region*, which is an instance of ``CacheRegion``, and defines the configuration
+  details for a particular cache backend.
+* A *backend*, which is an instance of ``CacheBackend``, describing how values
+  are stored and retrieved from a backend.  This interface specifies only
+  ``get()``, ``put()`` and ``delete()``.
+* Value generation functions.   These are user-defined functions that generate
+  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::
+
+    from dogpile.cache import make_region
+
+    region = make_region(
+        'dogpile.cache.pylibmc',
+        expiration_time = 3600,
+        arguments = {
+            'url':["127.0.0.1"],
+            'binary':True,
+            'behaviors':{"tcp_nodelay": True,"ketama":True}
+        }
+    )
+
+    @region.cache_on_arguments
+    def load_user_info(user_id):
+        return some_database.lookup_user_by_id(user_id)
+
+Above, we create a ``CacheRegion`` using the ``make_region()`` function.  This 
+function accepts as arguments the name of the backend as the only required argument,
+in this case ``dogpile.cache.pylibmc``.
+
+Subsequent arguments then include *expiration_time*, which is the expiration 
+time passed to the Dogpile lock, and *arguments*, which are arguments used directly
+by the backend - in this case we are using arguments that are passed directly
+to the pylibmc module.
+
+Backends
+--------
+
+Backends are located using the setuptools entrypoint system.  To make life easier
+for writers of ad-hoc backends, a helper function is included which registers any
+backend in the same way as if it were part of the existing sys.path.
+
+For example, to create a backend called ``DictionaryBackend``, we subclass
+``CacheBackend``::
+
+    from dogpile.cache import CacheBackend, NO_VALUE
+
+    class DictionaryBackend(CacheBackend):
+        def __init__(self, arguments):
+            self.cache = {}
+
+        def get(self, key):
+            return self.cache.get(key, NO_VALUE)
+
+        def put(self, key, value):
+            self.cache[key] = value
+
+        def delete(self, key):
+            self.cache.pop(key)
+
+Then make sure the class is available underneath the entrypoint
+``dogpile.cache``.  If we did this in a ``setup.py`` file, it would be 
+in ``setup()`` as::
+
+    entry_points="""
+      [dogpile.cache]
+      dictionary = mypackage.mybackend:DictionaryBackend
+      """
+
+Alternatively, if we want to register the plugin in the same process 
+space without bothering to install anything, we can use ``register_backend``::
+
+    from dogpile.cache import register_backend
+
+    register_backend("dictionary", "mypackage.mybackend", "DictionaryBackend")
+
+Our new backend would be usable in a region like this::
+
+    from dogpile.cache import make_region
+
+    region = make_region("dictionary")
+
+    data = region.put("somekey", "somevalue")
+
+Region Arguments
+----------------
+
+The ``make_region()`` function accepts these arguments::
+
+``name``
+
+  Required.  This is the name of the ``CacheBackend`` to use, and
+  is resolved by loading the class from the ``dogpile.cache`` entrypoint.
+
+``expiration_time``
+
+  Optional.  The expiration time passed to the dogpile system.  The ``get_or_create()``
+  method as well as the ``cache_on_arguments()`` decorator (note:  **not** the
+  ``get()`` method) will call upon the value creation function after this
+  time period has passed since the last generation.
+
+``arguments``
+
+  Optional.  The structure here is passed directly to the constructor
+  of the ``CacheBackend`` in use, though is typically a dictionary.
+
+``function_key_generator``
+
+  Optional.  Plug in a function that will produce a "cache key" given 
+  a data creation function and arguments.   The structure of this function
+  should be two levels: given the data creation function, return a new
+  function that generates the key based on the given arguments.  Such
+  as::
+
+    def my_key_generator(fn):
+        namespace = fn.__name__
+        def generate_key(*arg):
+            return namespace + "_".join(str(s) for s in arg)
+        return generate_key
+
+
+    region = make_region(
+        "dogpile.cache.dbm",
+        expiration_time=300,
+        arguments={
+            "filename":"file.dbm"
+        },
+        function_key_generator = my_key_generator
+
+    )
+
+Using a Region
+--------------
+
+The ``CacheRegion`` object is our front-end interface to a cache.  It includes
+the following methods:
+
+``get(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.
+
+``get_or_create(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.
+
+``put(key, value)``
+
+  Place a new value in the cache under the given key.
+
+``delete(key)``
+
+  Remove a value from the cache.   This operation is idempotent (can be
+  called multiple times, or on a non-existent key, safely)
+
+``cache_on_arguments(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.
+
+
+Mako Integration
+----------------
+
+dogpile.cache includes a Mako plugin that replaces Beaker as the cache backend.
+Simply setup a Mako template lookup using the "dogpile.cache" cache implementation
+and a region dictionary::
+
+    from dogpile.cache import make_region
+    from mako.lookup import TemplateLookup
+
+    my_regions = {
+        "local":make_region(
+                    "dogpile.cache.dbm", 
+                    expiration_time=360,
+                    arguments={"filename":"file.dbm"}
+                )
+        "memcached":make_region(
+                    "dogpile.cache.pylibmc", 
+                    expiration_time=3600,
+                    arguments={"url":["127.0.0.1"]}
+                )
+    }
+
+    mako_lookup = TemplateLookup(
+        directories=["/myapp/templates"],
+        cache_impl="dogpile.cache",
+        cache_regions=my_regions
+    )
+
+To use the above configuration in a template, use the ``cached=True`` argument on any
+Mako tag which accepts it, in conjunction with the name of the desired region
+as the ``cache_region`` argument::
+
+    <%def name="mysection()" cached=True cache_region="memcached">
+        some content that's cached
+    </%def>

dogpile/__init__.py

+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)

dogpile/cache/__init__.py

+__version__ = '0.1.0'
Add a comment to this file

dogpile/cache/backends/__init__.py

Empty file added.

Add a comment to this file

dogpile/cache/plugins/__init__.py

Empty file added.

+[egg_info]
+tag_build = dev
+
+[nosetests]
+logging-config=nose_logging_config.ini
+import os
+import sys
+
+from setuptools import setup, find_packages
+
+extra = {}
+if sys.version_info >= (3, 0):
+    extra.update(
+        use_2to3=True,
+    )
+
+readme = os.path.join(os.path.dirname(__file__), 'README.rst')
+
+setup(name='dogpile.cache',
+      version="0.1.0",
+      description="A caching front-end based on the Dogpile lock.",
+      long_description=file(readme).read(),
+      classifiers=[
+      'Development Status :: 3 - Alpha',
+      'Intended Audience :: Developers',
+      'License :: OSI Approved :: BSD License',
+      'Programming Language :: Python',
+      'Programming Language :: Python :: 3',
+      ],
+      keywords='caching',
+      author='Mike Bayer',
+      author_email='mike_mp@zzzcomputing.com',
+      url='http://bitbucket.org/zzzeek/dogpile.cache',
+      license='BSD',
+      packages=find_packages(exclude=['ez_setup', 'tests']),
+      namespace_packages=['dogpile'],
+      entry_points="""
+      [mako.cache]
+      dogpile = dogpile.cache.plugins.mako_plugin:MakoPlugin
+      """
+      zip_safe=False,
+      install_requires=['dogpile>=0.1.0'],
+      test_suite='nose.collector',
+      tests_require=['nose'],
+      **extra
+)
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.