Revert of Roll src/third_party/WebKit e0eac24:489c548 (svn 193311:193320) (patchset...
[chromium-blink-merge.git] / third_party / jinja2 / bccache.py
blobf2f9db61b31c552e2ebac965004330ba9ff517b0
1 # -*- coding: utf-8 -*-
2 """
3 jinja2.bccache
4 ~~~~~~~~~~~~~~
6 This module implements the bytecode cache system Jinja is optionally
7 using. This is useful if you have very complex template situations and
8 the compiliation of all those templates slow down your application too
9 much.
11 Situations where this is useful are often forking web applications that
12 are initialized on the first request.
14 :copyright: (c) 2010 by the Jinja Team.
15 :license: BSD.
16 """
17 from os import path, listdir
18 import sys
19 import marshal
20 import tempfile
21 import fnmatch
22 from hashlib import sha1
23 from jinja2.utils import open_if_exists
24 from jinja2._compat import BytesIO, pickle, PY2, text_type
27 # marshal works better on 3.x, one hack less required
28 if not PY2:
29 marshal_dump = marshal.dump
30 marshal_load = marshal.load
31 else:
33 def marshal_dump(code, f):
34 if isinstance(f, file):
35 marshal.dump(code, f)
36 else:
37 f.write(marshal.dumps(code))
39 def marshal_load(f):
40 if isinstance(f, file):
41 return marshal.load(f)
42 return marshal.loads(f.read())
45 bc_version = 2
47 # magic version used to only change with new jinja versions. With 2.6
48 # we change this to also take Python version changes into account. The
49 # reason for this is that Python tends to segfault if fed earlier bytecode
50 # versions because someone thought it would be a good idea to reuse opcodes
51 # or make Python incompatible with earlier versions.
52 bc_magic = 'j2'.encode('ascii') + \
53 pickle.dumps(bc_version, 2) + \
54 pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
57 class Bucket(object):
58 """Buckets are used to store the bytecode for one template. It's created
59 and initialized by the bytecode cache and passed to the loading functions.
61 The buckets get an internal checksum from the cache assigned and use this
62 to automatically reject outdated cache material. Individual bytecode
63 cache subclasses don't have to care about cache invalidation.
64 """
66 def __init__(self, environment, key, checksum):
67 self.environment = environment
68 self.key = key
69 self.checksum = checksum
70 self.reset()
72 def reset(self):
73 """Resets the bucket (unloads the bytecode)."""
74 self.code = None
76 def load_bytecode(self, f):
77 """Loads bytecode from a file or file like object."""
78 # make sure the magic header is correct
79 magic = f.read(len(bc_magic))
80 if magic != bc_magic:
81 self.reset()
82 return
83 # the source code of the file changed, we need to reload
84 checksum = pickle.load(f)
85 if self.checksum != checksum:
86 self.reset()
87 return
88 self.code = marshal_load(f)
90 def write_bytecode(self, f):
91 """Dump the bytecode into the file or file like object passed."""
92 if self.code is None:
93 raise TypeError('can\'t write empty bucket')
94 f.write(bc_magic)
95 pickle.dump(self.checksum, f, 2)
96 marshal_dump(self.code, f)
98 def bytecode_from_string(self, string):
99 """Load bytecode from a string."""
100 self.load_bytecode(BytesIO(string))
102 def bytecode_to_string(self):
103 """Return the bytecode as string."""
104 out = BytesIO()
105 self.write_bytecode(out)
106 return out.getvalue()
109 class BytecodeCache(object):
110 """To implement your own bytecode cache you have to subclass this class
111 and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
112 these methods are passed a :class:`~jinja2.bccache.Bucket`.
114 A very basic bytecode cache that saves the bytecode on the file system::
116 from os import path
118 class MyCache(BytecodeCache):
120 def __init__(self, directory):
121 self.directory = directory
123 def load_bytecode(self, bucket):
124 filename = path.join(self.directory, bucket.key)
125 if path.exists(filename):
126 with open(filename, 'rb') as f:
127 bucket.load_bytecode(f)
129 def dump_bytecode(self, bucket):
130 filename = path.join(self.directory, bucket.key)
131 with open(filename, 'wb') as f:
132 bucket.write_bytecode(f)
134 A more advanced version of a filesystem based bytecode cache is part of
135 Jinja2.
138 def load_bytecode(self, bucket):
139 """Subclasses have to override this method to load bytecode into a
140 bucket. If they are not able to find code in the cache for the
141 bucket, it must not do anything.
143 raise NotImplementedError()
145 def dump_bytecode(self, bucket):
146 """Subclasses have to override this method to write the bytecode
147 from a bucket back to the cache. If it unable to do so it must not
148 fail silently but raise an exception.
150 raise NotImplementedError()
152 def clear(self):
153 """Clears the cache. This method is not used by Jinja2 but should be
154 implemented to allow applications to clear the bytecode cache used
155 by a particular environment.
158 def get_cache_key(self, name, filename=None):
159 """Returns the unique hash key for this template name."""
160 hash = sha1(name.encode('utf-8'))
161 if filename is not None:
162 filename = '|' + filename
163 if isinstance(filename, text_type):
164 filename = filename.encode('utf-8')
165 hash.update(filename)
166 return hash.hexdigest()
168 def get_source_checksum(self, source):
169 """Returns a checksum for the source."""
170 return sha1(source.encode('utf-8')).hexdigest()
172 def get_bucket(self, environment, name, filename, source):
173 """Return a cache bucket for the given template. All arguments are
174 mandatory but filename may be `None`.
176 key = self.get_cache_key(name, filename)
177 checksum = self.get_source_checksum(source)
178 bucket = Bucket(environment, key, checksum)
179 self.load_bytecode(bucket)
180 return bucket
182 def set_bucket(self, bucket):
183 """Put the bucket into the cache."""
184 self.dump_bytecode(bucket)
187 class FileSystemBytecodeCache(BytecodeCache):
188 """A bytecode cache that stores bytecode on the filesystem. It accepts
189 two arguments: The directory where the cache items are stored and a
190 pattern string that is used to build the filename.
192 If no directory is specified the system temporary items folder is used.
194 The pattern can be used to have multiple separate caches operate on the
195 same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
196 is replaced with the cache key.
198 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
200 This bytecode cache supports clearing of the cache using the clear method.
203 def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
204 if directory is None:
205 directory = tempfile.gettempdir()
206 self.directory = directory
207 self.pattern = pattern
209 def _get_cache_filename(self, bucket):
210 return path.join(self.directory, self.pattern % bucket.key)
212 def load_bytecode(self, bucket):
213 f = open_if_exists(self._get_cache_filename(bucket), 'rb')
214 if f is not None:
215 try:
216 bucket.load_bytecode(f)
217 finally:
218 f.close()
220 def dump_bytecode(self, bucket):
221 f = open(self._get_cache_filename(bucket), 'wb')
222 try:
223 bucket.write_bytecode(f)
224 finally:
225 f.close()
227 def clear(self):
228 # imported lazily here because google app-engine doesn't support
229 # write access on the file system and the function does not exist
230 # normally.
231 from os import remove
232 files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
233 for filename in files:
234 try:
235 remove(path.join(self.directory, filename))
236 except OSError:
237 pass
240 class MemcachedBytecodeCache(BytecodeCache):
241 """This class implements a bytecode cache that uses a memcache cache for
242 storing the information. It does not enforce a specific memcache library
243 (tummy's memcache or cmemcache) but will accept any class that provides
244 the minimal interface required.
246 Libraries compatible with this class:
248 - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
249 - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
250 - `cmemcache <http://gijsbert.org/cmemcache/>`_
252 (Unfortunately the django cache interface is not compatible because it
253 does not support storing binary data, only unicode. You can however pass
254 the underlying cache client to the bytecode cache which is available
255 as `django.core.cache.cache._client`.)
257 The minimal interface for the client passed to the constructor is this:
259 .. class:: MinimalClientInterface
261 .. method:: set(key, value[, timeout])
263 Stores the bytecode in the cache. `value` is a string and
264 `timeout` the timeout of the key. If timeout is not provided
265 a default timeout or no timeout should be assumed, if it's
266 provided it's an integer with the number of seconds the cache
267 item should exist.
269 .. method:: get(key)
271 Returns the value for the cache key. If the item does not
272 exist in the cache the return value must be `None`.
274 The other arguments to the constructor are the prefix for all keys that
275 is added before the actual cache key and the timeout for the bytecode in
276 the cache system. We recommend a high (or no) timeout.
278 This bytecode cache does not support clearing of used items in the cache.
279 The clear method is a no-operation function.
281 .. versionadded:: 2.7
282 Added support for ignoring memcache errors through the
283 `ignore_memcache_errors` parameter.
286 def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
287 ignore_memcache_errors=True):
288 self.client = client
289 self.prefix = prefix
290 self.timeout = timeout
291 self.ignore_memcache_errors = ignore_memcache_errors
293 def load_bytecode(self, bucket):
294 try:
295 code = self.client.get(self.prefix + bucket.key)
296 except Exception:
297 if not self.ignore_memcache_errors:
298 raise
299 code = None
300 if code is not None:
301 bucket.bytecode_from_string(code)
303 def dump_bytecode(self, bucket):
304 args = (self.prefix + bucket.key, bucket.bytecode_to_string())
305 if self.timeout is not None:
306 args += (self.timeout,)
307 try:
308 self.client.set(*args)
309 except Exception:
310 if not self.ignore_memcache_errors:
311 raise