blob: 93e1041ebc0ee93b3654322758751f34a92f8471 [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 Ronacher4d5bdff2008-09-17 16:19:46 +020018import marshal
Armin Ronachera816bf42008-09-17 21:28:01 +020019import tempfile
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020020import cPickle as pickle
Armin Ronachera816bf42008-09-17 21:28:01 +020021import fnmatch
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020022from cStringIO import StringIO
23try:
24 from hashlib import sha1
25except ImportError:
26 from sha import new as sha1
Armin Ronacherccae0552008-10-05 23:08:58 +020027from jinja2.utils import open_if_exists
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020028
29
30bc_version = 1
Armin Ronacher42a19882009-08-05 18:45:39 +020031bc_magic = 'j2'.encode('ascii') + pickle.dumps(bc_version, 2)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020032
33
34class Bucket(object):
Armin Ronachera816bf42008-09-17 21:28:01 +020035 """Buckets are used to store the bytecode for one template. It's created
36 and initialized by the bytecode cache and passed to the loading functions.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020037
Armin Ronachera816bf42008-09-17 21:28:01 +020038 The buckets get an internal checksum from the cache assigned and use this
39 to automatically reject outdated cache material. Individual bytecode
40 cache subclasses don't have to care about cache invalidation.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020041 """
42
Armin Ronachera816bf42008-09-17 21:28:01 +020043 def __init__(self, environment, key, checksum):
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020044 self.environment = environment
45 self.key = key
46 self.checksum = checksum
47 self.reset()
48
49 def reset(self):
Armin Ronachera816bf42008-09-17 21:28:01 +020050 """Resets the bucket (unloads the bytecode)."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020051 self.code = None
52
Armin Ronachera816bf42008-09-17 21:28:01 +020053 def load_bytecode(self, f):
54 """Loads bytecode from a file or file like object."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020055 # make sure the magic header is correct
56 magic = f.read(len(bc_magic))
57 if magic != bc_magic:
58 self.reset()
59 return
60 # the source code of the file changed, we need to reload
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020061 checksum = pickle.load(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020062 if self.checksum != checksum:
63 self.reset()
64 return
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020065 # now load the code. Because marshal is not able to load
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020066 # from arbitrary streams we have to work around that
67 if isinstance(f, file):
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020068 self.code = marshal.load(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020069 else:
70 self.code = marshal.loads(f.read())
71
Armin Ronachera816bf42008-09-17 21:28:01 +020072 def write_bytecode(self, f):
73 """Dump the bytecode into the file or file like object passed."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020074 if self.code is None:
75 raise TypeError('can\'t write empty bucket')
76 f.write(bc_magic)
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020077 pickle.dump(self.checksum, f, 2)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020078 if isinstance(f, file):
Armin Ronacheraa1d17d2008-09-18 18:09:06 +020079 marshal.dump(self.code, f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020080 else:
81 f.write(marshal.dumps(self.code))
82
Armin Ronachera816bf42008-09-17 21:28:01 +020083 def bytecode_from_string(self, string):
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020084 """Load bytecode from a string."""
Armin Ronachera816bf42008-09-17 21:28:01 +020085 self.load_bytecode(StringIO(string))
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020086
Armin Ronachera816bf42008-09-17 21:28:01 +020087 def bytecode_to_string(self):
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020088 """Return the bytecode as string."""
89 out = StringIO()
Armin Ronachera816bf42008-09-17 21:28:01 +020090 self.write_bytecode(out)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020091 return out.getvalue()
92
Armin Ronacher4d5bdff2008-09-17 16:19:46 +020093
94class BytecodeCache(object):
95 """To implement your own bytecode cache you have to subclass this class
Armin Ronachera816bf42008-09-17 21:28:01 +020096 and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
97 these methods are passed a :class:`~jinja2.bccache.Bucket`.
98
99 A very basic bytecode cache that saves the bytecode on the file system::
100
101 from os import path
102
103 class MyCache(BytecodeCache):
104
105 def __init__(self, directory):
106 self.directory = directory
107
108 def load_bytecode(self, bucket):
109 filename = path.join(self.directory, bucket.key)
110 if path.exists(filename):
111 with file(filename, 'rb') as f:
112 bucket.load_bytecode(f)
113
114 def dump_bytecode(self, bucket):
115 filename = path.join(self.directory, bucket.key)
116 with file(filename, 'wb') as f:
117 bucket.write_bytecode(f)
118
119 A more advanced version of a filesystem based bytecode cache is part of
120 Jinja2.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200121 """
122
Armin Ronachera816bf42008-09-17 21:28:01 +0200123 def load_bytecode(self, bucket):
124 """Subclasses have to override this method to load bytecode into a
125 bucket. If they are not able to find code in the cache for the
126 bucket, it must not do anything.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200127 """
128 raise NotImplementedError()
129
Armin Ronachera816bf42008-09-17 21:28:01 +0200130 def dump_bytecode(self, bucket):
131 """Subclasses have to override this method to write the bytecode
132 from a bucket back to the cache. If it unable to do so it must not
133 fail silently but raise an exception.
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200134 """
135 raise NotImplementedError()
136
Armin Ronachera816bf42008-09-17 21:28:01 +0200137 def clear(self):
138 """Clears the cache. This method is not used by Jinja2 but should be
139 implemented to allow applications to clear the bytecode cache used
140 by a particular environment.
141 """
142
143 def get_cache_key(self, name, filename=None):
144 """Returns the unique hash key for this template name."""
145 hash = sha1(name.encode('utf-8'))
146 if filename is not None:
147 if isinstance(filename, unicode):
148 filename = filename.encode('utf-8')
149 hash.update('|' + filename)
150 return hash.hexdigest()
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200151
152 def get_source_checksum(self, source):
Armin Ronachera816bf42008-09-17 21:28:01 +0200153 """Returns a checksum for the source."""
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200154 return sha1(source.encode('utf-8')).hexdigest()
155
Armin Ronachera816bf42008-09-17 21:28:01 +0200156 def get_bucket(self, environment, name, filename, source):
157 """Return a cache bucket for the given template. All arguments are
158 mandatory but filename may be `None`.
159 """
160 key = self.get_cache_key(name, filename)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200161 checksum = self.get_source_checksum(source)
Armin Ronachera816bf42008-09-17 21:28:01 +0200162 bucket = Bucket(environment, key, checksum)
163 self.load_bytecode(bucket)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200164 return bucket
165
Armin Ronachera816bf42008-09-17 21:28:01 +0200166 def set_bucket(self, bucket):
167 """Put the bucket into the cache."""
168 self.dump_bytecode(bucket)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200169
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200170
Armin Ronachera816bf42008-09-17 21:28:01 +0200171class FileSystemBytecodeCache(BytecodeCache):
172 """A bytecode cache that stores bytecode on the filesystem. It accepts
173 two arguments: The directory where the cache items are stored and a
174 pattern string that is used to build the filename.
175
176 If no directory is specified the system temporary items folder is used.
177
178 The pattern can be used to have multiple separate caches operate on the
179 same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
180 is replaced with the cache key.
181
182 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200183
184 This bytecode cache supports clearing of the cache using the clear method.
Armin Ronachera816bf42008-09-17 21:28:01 +0200185 """
186
187 def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
188 if directory is None:
189 directory = tempfile.gettempdir()
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200190 self.directory = directory
191 self.pattern = pattern
192
193 def _get_cache_filename(self, bucket):
194 return path.join(self.directory, self.pattern % bucket.key)
195
Armin Ronachera816bf42008-09-17 21:28:01 +0200196 def load_bytecode(self, bucket):
Armin Ronacherccae0552008-10-05 23:08:58 +0200197 f = open_if_exists(self._get_cache_filename(bucket), 'rb')
198 if f is not None:
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200199 try:
Armin Ronachera816bf42008-09-17 21:28:01 +0200200 bucket.load_bytecode(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200201 finally:
202 f.close()
203
Armin Ronachera816bf42008-09-17 21:28:01 +0200204 def dump_bytecode(self, bucket):
Armin Ronacherccae0552008-10-05 23:08:58 +0200205 f = file(self._get_cache_filename(bucket), 'wb')
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200206 try:
Armin Ronachera816bf42008-09-17 21:28:01 +0200207 bucket.write_bytecode(f)
Armin Ronacher4d5bdff2008-09-17 16:19:46 +0200208 finally:
209 f.close()
Armin Ronachera816bf42008-09-17 21:28:01 +0200210
211 def clear(self):
Max Ischenko03f88232008-09-18 16:23:33 +0200212 # imported lazily here because google app-engine doesn't support
213 # write access on the file system and the function does not exist
214 # normally.
215 from os import remove
Armin Ronacher2e46a5c2008-09-17 22:25:04 +0200216 files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
217 for filename in files:
Armin Ronachera816bf42008-09-17 21:28:01 +0200218 try:
219 remove(path.join(self.directory, filename))
220 except OSError:
221 pass
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200222
223
224class MemcachedBytecodeCache(BytecodeCache):
225 """This class implements a bytecode cache that uses a memcache cache for
226 storing the information. It does not enforce a specific memcache library
227 (tummy's memcache or cmemcache) but will accept any class that provides
228 the minimal interface required.
229
230 Libraries compatible with this class:
231
232 - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
233 - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
234 - `cmemcache <http://gijsbert.org/cmemcache/>`_
235
236 (Unfortunately the django cache interface is not compatible because it
237 does not support storing binary data, only unicode. You can however pass
Georg Brandl3e497b72008-09-19 09:55:17 +0000238 the underlying cache client to the bytecode cache which is available
Armin Ronacheraa1d17d2008-09-18 18:09:06 +0200239 as `django.core.cache.cache._client`.)
240
241 The minimal interface for the client passed to the constructor is this:
242
243 .. class:: MinimalClientInterface
244
245 .. method:: set(key, value[, timeout])
246
247 Stores the bytecode in the cache. `value` is a string and
248 `timeout` the timeout of the key. If timeout is not provided
249 a default timeout or no timeout should be assumed, if it's
250 provided it's an integer with the number of seconds the cache
251 item should exist.
252
253 .. method:: get(key)
254
255 Returns the value for the cache key. If the item does not
256 exist in the cache the return value must be `None`.
257
258 The other arguments to the constructor are the prefix for all keys that
259 is added before the actual cache key and the timeout for the bytecode in
260 the cache system. We recommend a high (or no) timeout.
261
262 This bytecode cache does not support clearing of used items in the cache.
263 The clear method is a no-operation function.
264 """
265
266 def __init__(self, client, prefix='jinja2/bytecode/', timeout=None):
267 self.client = client
268 self.prefix = prefix
269 self.timeout = timeout
270
271 def load_bytecode(self, bucket):
272 code = self.client.get(self.prefix + bucket.key)
273 if code is not None:
274 bucket.bytecode_from_string(code)
275
276 def dump_bytecode(self, bucket):
277 args = (self.prefix + bucket.key, bucket.bytecode_to_string())
278 if self.timeout is not None:
279 args += (self.timeout,)
280 self.client.set(*args)