blob: f02291801e91c2263121b855b6d033a916c2e397 [file] [log] [blame]
Armin Ronacher4d5bdff2008-09-17 16:19:46 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.bccache
4 ~~~~~~~~~~~~~~
5
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.
10
11 Situations where this is useful are often forking web applications that
12 are initialized on the first request.
13
Armin Ronacher55494e42010-01-22 09:41:48 +010014 :copyright: (c) 2010 by the Jinja Team.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020015 :license: BSD.
16"""
Max Ischenko03f88232008-09-18 16:23:33 +020017from os import path, listdir
Armin Ronacher51db6c92011-01-11 20:53:42 +010018import sys
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020019import marshal
Armin Ronachera816bf42008-09-17 21:28:01 +020020import tempfile
Thomas Waldmann7d295622013-05-18 00:06:22 +020021from six.moves import cPickle as pickle
Cory Benfield0d3b3892013-05-18 11:58:18 +010022from six import BytesIO
Armin Ronachera816bf42008-09-17 21:28:01 +020023import fnmatch
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020024try:
25 from hashlib import sha1
26except ImportError:
27 from sha import new as sha1
Armin Ronacherccae0552008-10-05 23:08:58 +020028from jinja2.utils import open_if_exists
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020029
30
Armin Ronacher086174c2011-05-24 17:50:08 +020031# marshal works better on 3.x, one hack less required
Cory Benfieldb36039f2013-05-18 11:40:52 +010032if sys.version_info[0] >= 3:
Armin Ronacher086174c2011-05-24 17:50:08 +020033 marshal_dump = marshal.dump
34 marshal_load = marshal.load
35else:
Armin Ronacher086174c2011-05-24 17:50:08 +020036
37 def marshal_dump(code, f):
38 if isinstance(f, file):
39 marshal.dump(code, f)
40 else:
41 f.write(marshal.dumps(code))
42
43 def marshal_load(f):
44 if isinstance(f, file):
45 return marshal.load(f)
46 return marshal.loads(f.read())
47
48
Armin Ronacher51db6c92011-01-11 20:53:42 +010049bc_version = 2
50
51# magic version used to only change with new jinja versions. With 2.6
52# we change this to also take Python version changes into account. The
53# reason for this is that Python tends to segfault if fed earlier bytecode
54# versions because someone thought it would be a good idea to reuse opcodes
55# or make Python incompatible with earlier versions.
56bc_magic = 'j2'.encode('ascii') + \
57 pickle.dumps(bc_version, 2) + \
58 pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020059
60
61class Bucket(object):
Armin Ronachera816bf42008-09-17 21:28:01 +020062 """Buckets are used to store the bytecode for one template. It's created
63 and initialized by the bytecode cache and passed to the loading functions.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020064
Armin Ronachera816bf42008-09-17 21:28:01 +020065 The buckets get an internal checksum from the cache assigned and use this
66 to automatically reject outdated cache material. Individual bytecode
67 cache subclasses don't have to care about cache invalidation.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020068 """
69
Armin Ronachera816bf42008-09-17 21:28:01 +020070 def __init__(self, environment, key, checksum):
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020071 self.environment = environment
72 self.key = key
73 self.checksum = checksum
74 self.reset()
75
76 def reset(self):
Armin Ronachera816bf42008-09-17 21:28:01 +020077 """Resets the bucket (unloads the bytecode)."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020078 self.code = None
79
Armin Ronachera816bf42008-09-17 21:28:01 +020080 def load_bytecode(self, f):
81 """Loads bytecode from a file or file like object."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020082 # make sure the magic header is correct
83 magic = f.read(len(bc_magic))
84 if magic != bc_magic:
85 self.reset()
86 return
87 # the source code of the file changed, we need to reload
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020088 checksum = pickle.load(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020089 if self.checksum != checksum:
90 self.reset()
91 return
Armin Ronacher086174c2011-05-24 17:50:08 +020092 self.code = marshal_load(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020093
Armin Ronachera816bf42008-09-17 21:28:01 +020094 def write_bytecode(self, f):
95 """Dump the bytecode into the file or file like object passed."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020096 if self.code is None:
97 raise TypeError('can\'t write empty bucket')
98 f.write(bc_magic)
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020099 pickle.dump(self.checksum, f, 2)
Marcin Mincereeea4952011-07-12 01:59:54 -0700100 marshal_dump(self.code, f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200101
Armin Ronachera816bf42008-09-17 21:28:01 +0200102 def bytecode_from_string(self, string):
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200103 """Load bytecode from a string."""
Armin Ronacher086174c2011-05-24 17:50:08 +0200104 self.load_bytecode(BytesIO(string))
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200105
Armin Ronachera816bf42008-09-17 21:28:01 +0200106 def bytecode_to_string(self):
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200107 """Return the bytecode as string."""
Armin Ronacher086174c2011-05-24 17:50:08 +0200108 out = BytesIO()
Armin Ronachera816bf42008-09-17 21:28:01 +0200109 self.write_bytecode(out)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200110 return out.getvalue()
111
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200112
113class BytecodeCache(object):
114 """To implement your own bytecode cache you have to subclass this class
Armin Ronachera816bf42008-09-17 21:28:01 +0200115 and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
116 these methods are passed a :class:`~jinja2.bccache.Bucket`.
117
118 A very basic bytecode cache that saves the bytecode on the file system::
119
120 from os import path
121
122 class MyCache(BytecodeCache):
123
124 def __init__(self, directory):
125 self.directory = directory
126
127 def load_bytecode(self, bucket):
128 filename = path.join(self.directory, bucket.key)
129 if path.exists(filename):
Armin Ronacher0faa8612010-02-09 15:04:51 +0100130 with open(filename, 'rb') as f:
Armin Ronachera816bf42008-09-17 21:28:01 +0200131 bucket.load_bytecode(f)
132
133 def dump_bytecode(self, bucket):
134 filename = path.join(self.directory, bucket.key)
Armin Ronacher0faa8612010-02-09 15:04:51 +0100135 with open(filename, 'wb') as f:
Armin Ronachera816bf42008-09-17 21:28:01 +0200136 bucket.write_bytecode(f)
137
138 A more advanced version of a filesystem based bytecode cache is part of
139 Jinja2.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200140 """
141
Armin Ronachera816bf42008-09-17 21:28:01 +0200142 def load_bytecode(self, bucket):
143 """Subclasses have to override this method to load bytecode into a
144 bucket. If they are not able to find code in the cache for the
145 bucket, it must not do anything.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200146 """
147 raise NotImplementedError()
148
Armin Ronachera816bf42008-09-17 21:28:01 +0200149 def dump_bytecode(self, bucket):
150 """Subclasses have to override this method to write the bytecode
151 from a bucket back to the cache. If it unable to do so it must not
152 fail silently but raise an exception.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200153 """
154 raise NotImplementedError()
155
Armin Ronachera816bf42008-09-17 21:28:01 +0200156 def clear(self):
157 """Clears the cache. This method is not used by Jinja2 but should be
158 implemented to allow applications to clear the bytecode cache used
159 by a particular environment.
160 """
161
162 def get_cache_key(self, name, filename=None):
163 """Returns the unique hash key for this template name."""
164 hash = sha1(name.encode('utf-8'))
165 if filename is not None:
Armin Ronacher086174c2011-05-24 17:50:08 +0200166 filename = '|' + filename
Armin Ronachera816bf42008-09-17 21:28:01 +0200167 if isinstance(filename, unicode):
168 filename = filename.encode('utf-8')
Armin Ronacher086174c2011-05-24 17:50:08 +0200169 hash.update(filename)
Armin Ronachera816bf42008-09-17 21:28:01 +0200170 return hash.hexdigest()
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200171
172 def get_source_checksum(self, source):
Armin Ronachera816bf42008-09-17 21:28:01 +0200173 """Returns a checksum for the source."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200174 return sha1(source.encode('utf-8')).hexdigest()
175
Armin Ronachera816bf42008-09-17 21:28:01 +0200176 def get_bucket(self, environment, name, filename, source):
177 """Return a cache bucket for the given template. All arguments are
178 mandatory but filename may be `None`.
179 """
180 key = self.get_cache_key(name, filename)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200181 checksum = self.get_source_checksum(source)
Armin Ronachera816bf42008-09-17 21:28:01 +0200182 bucket = Bucket(environment, key, checksum)
183 self.load_bytecode(bucket)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200184 return bucket
185
Armin Ronachera816bf42008-09-17 21:28:01 +0200186 def set_bucket(self, bucket):
187 """Put the bucket into the cache."""
188 self.dump_bytecode(bucket)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200189
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200190
Armin Ronachera816bf42008-09-17 21:28:01 +0200191class FileSystemBytecodeCache(BytecodeCache):
192 """A bytecode cache that stores bytecode on the filesystem. It accepts
193 two arguments: The directory where the cache items are stored and a
194 pattern string that is used to build the filename.
195
196 If no directory is specified the system temporary items folder is used.
197
198 The pattern can be used to have multiple separate caches operate on the
199 same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
200 is replaced with the cache key.
201
202 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200203
204 This bytecode cache supports clearing of the cache using the clear method.
Armin Ronachera816bf42008-09-17 21:28:01 +0200205 """
206
207 def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
208 if directory is None:
209 directory = tempfile.gettempdir()
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200210 self.directory = directory
211 self.pattern = pattern
212
213 def _get_cache_filename(self, bucket):
214 return path.join(self.directory, self.pattern % bucket.key)
215
Armin Ronachera816bf42008-09-17 21:28:01 +0200216 def load_bytecode(self, bucket):
Armin Ronacherccae0552008-10-05 23:08:58 +0200217 f = open_if_exists(self._get_cache_filename(bucket), 'rb')
218 if f is not None:
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200219 try:
Armin Ronachera816bf42008-09-17 21:28:01 +0200220 bucket.load_bytecode(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200221 finally:
222 f.close()
223
Armin Ronachera816bf42008-09-17 21:28:01 +0200224 def dump_bytecode(self, bucket):
Armin Ronacher0faa8612010-02-09 15:04:51 +0100225 f = open(self._get_cache_filename(bucket), 'wb')
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200226 try:
Armin Ronachera816bf42008-09-17 21:28:01 +0200227 bucket.write_bytecode(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200228 finally:
229 f.close()
Armin Ronachera816bf42008-09-17 21:28:01 +0200230
231 def clear(self):
Max Ischenko03f88232008-09-18 16:23:33 +0200232 # imported lazily here because google app-engine doesn't support
233 # write access on the file system and the function does not exist
234 # normally.
235 from os import remove
Armin Ronacher2e46a5c2008-09-17 22:25:04 +0200236 files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
237 for filename in files:
Armin Ronachera816bf42008-09-17 21:28:01 +0200238 try:
239 remove(path.join(self.directory, filename))
240 except OSError:
241 pass
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200242
243
244class MemcachedBytecodeCache(BytecodeCache):
245 """This class implements a bytecode cache that uses a memcache cache for
246 storing the information. It does not enforce a specific memcache library
247 (tummy's memcache or cmemcache) but will accept any class that provides
248 the minimal interface required.
249
250 Libraries compatible with this class:
251
252 - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
253 - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
254 - `cmemcache <http://gijsbert.org/cmemcache/>`_
255
256 (Unfortunately the django cache interface is not compatible because it
257 does not support storing binary data, only unicode. You can however pass
Georg Brandl3e497b72008-09-19 09:55:17 +0000258 the underlying cache client to the bytecode cache which is available
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200259 as `django.core.cache.cache._client`.)
260
261 The minimal interface for the client passed to the constructor is this:
262
263 .. class:: MinimalClientInterface
264
265 .. method:: set(key, value[, timeout])
266
267 Stores the bytecode in the cache. `value` is a string and
268 `timeout` the timeout of the key. If timeout is not provided
269 a default timeout or no timeout should be assumed, if it's
270 provided it's an integer with the number of seconds the cache
271 item should exist.
272
273 .. method:: get(key)
274
275 Returns the value for the cache key. If the item does not
276 exist in the cache the return value must be `None`.
277
278 The other arguments to the constructor are the prefix for all keys that
279 is added before the actual cache key and the timeout for the bytecode in
280 the cache system. We recommend a high (or no) timeout.
281
282 This bytecode cache does not support clearing of used items in the cache.
283 The clear method is a no-operation function.
Armin Ronacher840e7e02013-05-19 11:06:18 +0100284
285 .. versionadded:: 2.7
286 Added support for ignoring memcache errors through the
287 `ignore_memcache_errors` parameter.
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200288 """
289
Armin Ronacher840e7e02013-05-19 11:06:18 +0100290 def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
291 ignore_memcache_errors=True):
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200292 self.client = client
293 self.prefix = prefix
294 self.timeout = timeout
Armin Ronacher840e7e02013-05-19 11:06:18 +0100295 self.ignore_memcache_errors = ignore_memcache_errors
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200296
297 def load_bytecode(self, bucket):
Kyle Adams5871ba82013-02-18 13:26:40 -0500298 try:
299 code = self.client.get(self.prefix + bucket.key)
Armin Ronacher840e7e02013-05-19 11:06:18 +0100300 except Exception:
301 if not self.ignore_memcache_errors:
302 raise
Kyle Adams5871ba82013-02-18 13:26:40 -0500303 code = None
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200304 if code is not None:
305 bucket.bytecode_from_string(code)
306
307 def dump_bytecode(self, bucket):
308 args = (self.prefix + bucket.key, bucket.bytecode_to_string())
309 if self.timeout is not None:
310 args += (self.timeout,)
Kyle Adams5871ba82013-02-18 13:26:40 -0500311 try:
312 self.client.set(*args)
Armin Ronacher840e7e02013-05-19 11:06:18 +0100313 except Exception:
314 if not self.ignore_memcache_errors:
315 raise