Fixed bytecode cache and added support for memcached (tests still missing)
authorArmin Ronacher <armin.ronacher@active-4.com>
Thu, 18 Sep 2008 16:09:06 +0000 (18:09 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Thu, 18 Sep 2008 16:09:06 +0000 (18:09 +0200)
--HG--
branch : trunk

docs/api.rst
jinja2/__init__.py
jinja2/bccache.py
jinja2/loaders.py

index b939eaa47a540c58e43d366f352c409021dee4ed..ef53321bf674283ca78baa77afa8df8a0aa81f82 100644 (file)
@@ -436,6 +436,8 @@ Builtin bytecode caches:
 
 .. autoclass:: jinja2.FileSystemBytecodeCache
 
+.. autoclass:: jinja2.MemcachedBytecodeCache
+
 
 Utilities
 ---------
index f7576d1f962a012a8a59490dca18814111c5b264..0a3ff6f3f9489c0b24f08bb0cae53f74ea576a94 100644 (file)
@@ -41,7 +41,8 @@ from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
      DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader
 
 # bytecode caches
-from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache
+from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
+     MemcachedBytecodeCache
 
 # undefined types
 from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
index 1a0e7ec7c984a5f1e13116f619937f9620ca859f..de6d2b402a3a35bfcecf1f0d05e8fb3cca2fdf86 100644 (file)
@@ -57,14 +57,14 @@ class Bucket(object):
             self.reset()
             return
         # the source code of the file changed, we need to reload
-        checksum = pickle.load_bytecode(f)
+        checksum = pickle.load(f)
         if self.checksum != checksum:
             self.reset()
             return
-        # now load_bytecode the code.  Because marshal is not able to load_bytecode
+        # now load the code.  Because marshal is not able to load
         # from arbitrary streams we have to work around that
         if isinstance(f, file):
-            self.code = marshal.load_bytecode(f)
+            self.code = marshal.load(f)
         else:
             self.code = marshal.loads(f.read())
 
@@ -73,9 +73,9 @@ class Bucket(object):
         if self.code is None:
             raise TypeError('can\'t write empty bucket')
         f.write(bc_magic)
-        pickle.write_bytecode(self.checksum, f, 2)
+        pickle.dump(self.checksum, f, 2)
         if isinstance(f, file):
-            marshal.write_bytecode(self.code, f)
+            marshal.dump(self.code, f)
         else:
             f.write(marshal.dumps(self.code))
 
@@ -179,6 +179,8 @@ class FileSystemBytecodeCache(BytecodeCache):
     is replaced with the cache key.
 
     >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
+
+    This bytecode cache supports clearing of the cache using the clear method.
     """
 
     def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
@@ -218,3 +220,62 @@ class FileSystemBytecodeCache(BytecodeCache):
                 remove(path.join(self.directory, filename))
             except OSError:
                 pass
+
+
+class MemcachedBytecodeCache(BytecodeCache):
+    """This class implements a bytecode cache that uses a memcache cache for
+    storing the information.  It does not enforce a specific memcache library
+    (tummy's memcache or cmemcache) but will accept any class that provides
+    the minimal interface required.
+
+    Libraries compatible with this class:
+
+    -   `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
+    -   `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
+    -   `cmemcache <http://gijsbert.org/cmemcache/>`_
+
+    (Unfortunately the django cache interface is not compatible because it
+    does not support storing binary data, only unicode.  You can however pass
+    the underlaying cache client to the bytecode cache which is available
+    as `django.core.cache.cache._client`.)
+
+    The minimal interface for the client passed to the constructor is this:
+
+    .. class:: MinimalClientInterface
+
+        .. method:: set(key, value[, timeout])
+
+            Stores the bytecode in the cache.  `value` is a string and
+            `timeout` the timeout of the key.  If timeout is not provided
+            a default timeout or no timeout should be assumed, if it's
+            provided it's an integer with the number of seconds the cache
+            item should exist.
+
+        .. method:: get(key)
+
+            Returns the value for the cache key.  If the item does not
+            exist in the cache the return value must be `None`.
+
+    The other arguments to the constructor are the prefix for all keys that
+    is added before the actual cache key and the timeout for the bytecode in
+    the cache system.  We recommend a high (or no) timeout.
+
+    This bytecode cache does not support clearing of used items in the cache.
+    The clear method is a no-operation function.
+    """
+
+    def __init__(self, client, prefix='jinja2/bytecode/', timeout=None):
+        self.client = client
+        self.prefix = prefix
+        self.timeout = timeout
+
+    def load_bytecode(self, bucket):
+        code = self.client.get(self.prefix + bucket.key)
+        if code is not None:
+            bucket.bytecode_from_string(code)
+
+    def dump_bytecode(self, bucket):
+        args = (self.prefix + bucket.key, bucket.bytecode_to_string())
+        if self.timeout is not None:
+            args += (self.timeout,)
+        self.client.set(*args)
index 264b12385d9a96f7039f9a9567381682a535668f..b0522cdce134bddce3a055fb276b338172464b7c 100644 (file)
@@ -96,8 +96,8 @@ class BaseLoader(object):
 
         # try to load the code from the bytecode cache if there is a
         # bytecode cache configured.
-        bbc = environment.bytecode_cache
-        if bbc is not None:
+        bcc = environment.bytecode_cache
+        if bcc is not None:
             bucket = bcc.get_bucket(environment, name, filename, source)
             code = bucket.code
 
@@ -109,9 +109,9 @@ class BaseLoader(object):
         # if the bytecode cache is available and the bucket doesn't
         # have a code so far, we give the bucket the new code and put
         # it back to the bytecode cache.
-        if bbc is not None and bucket.code is None:
+        if bcc is not None and bucket.code is None:
             bucket.code = code
-            bbc.set_bucket(bucket)
+            bcc.set_bucket(bucket)
 
         return environment.template_class.from_code(environment, code,
                                                     globals, uptodate)