From aa1d17d6ca2e4a9c049636b9dab826784186c297 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 18 Sep 2008 18:09:06 +0200 Subject: [PATCH] Fixed bytecode cache and added support for memcached (tests still missing) --HG-- branch : trunk --- docs/api.rst | 2 ++ jinja2/__init__.py | 3 +- jinja2/bccache.py | 71 ++++++++++++++++++++++++++++++++++++++++++---- jinja2/loaders.py | 8 +++--- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index b939eaa..ef53321 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -436,6 +436,8 @@ Builtin bytecode caches: .. autoclass:: jinja2.FileSystemBytecodeCache +.. autoclass:: jinja2.MemcachedBytecodeCache + Utilities --------- diff --git a/jinja2/__init__.py b/jinja2/__init__.py index f7576d1..0a3ff6f 100644 --- a/jinja2/__init__.py +++ b/jinja2/__init__.py @@ -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 diff --git a/jinja2/bccache.py b/jinja2/bccache.py index 1a0e7ec..de6d2b4 100644 --- a/jinja2/bccache.py +++ b/jinja2/bccache.py @@ -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 `_.contrib.cache + - `python-memcached `_ + - `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) diff --git a/jinja2/loaders.py b/jinja2/loaders.py index 264b123..b0522cd 100644 --- a/jinja2/loaders.py +++ b/jinja2/loaders.py @@ -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) -- 2.26.2