Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: mojo/public/third_party/jinja2/bccache.py

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # -*- 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
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
25
26
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:
32
33 def marshal_dump(code, f):
34 if isinstance(f, file):
35 marshal.dump(code, f)
36 else:
37 f.write(marshal.dumps(code))
38
39 def marshal_load(f):
40 if isinstance(f, file):
41 return marshal.load(f)
42 return marshal.loads(f.read())
43
44
45 bc_version = 2
46
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])
55
56
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.
60
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 """
65
66 def __init__(self, environment, key, checksum):
67 self.environment = environment
68 self.key = key
69 self.checksum = checksum
70 self.reset()
71
72 def reset(self):
73 """Resets the bucket (unloads the bytecode)."""
74 self.code = None
75
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)
89
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)
97
98 def bytecode_from_string(self, string):
99 """Load bytecode from a string."""
100 self.load_bytecode(BytesIO(string))
101
102 def bytecode_to_string(self):
103 """Return the bytecode as string."""
104 out = BytesIO()
105 self.write_bytecode(out)
106 return out.getvalue()
107
108
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`.
113
114 A very basic bytecode cache that saves the bytecode on the file system::
115
116 from os import path
117
118 class MyCache(BytecodeCache):
119
120 def __init__(self, directory):
121 self.directory = directory
122
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)
128
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)
133
134 A more advanced version of a filesystem based bytecode cache is part of
135 Jinja2.
136 """
137
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.
142 """
143 raise NotImplementedError()
144
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.
149 """
150 raise NotImplementedError()
151
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.
156 """
157
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()
167
168 def get_source_checksum(self, source):
169 """Returns a checksum for the source."""
170 return sha1(source.encode('utf-8')).hexdigest()
171
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`.
175 """
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
181
182 def set_bucket(self, bucket):
183 """Put the bucket into the cache."""
184 self.dump_bytecode(bucket)
185
186
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.
191
192 If no directory is specified the system temporary items folder is used.
193
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.
197
198 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
199
200 This bytecode cache supports clearing of the cache using the clear method.
201 """
202
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
208
209 def _get_cache_filename(self, bucket):
210 return path.join(self.directory, self.pattern % bucket.key)
211
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()
219
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()
226
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
238
239
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.
245
246 Libraries compatible with this class:
247
248 - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
249 - `python-memcached <http://www.tummy.com/Community/software/python-memcac hed/>`_
250 - `cmemcache <http://gijsbert.org/cmemcache/>`_
251
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`.)
256
257 The minimal interface for the client passed to the constructor is this:
258
259 .. class:: MinimalClientInterface
260
261 .. method:: set(key, value[, timeout])
262
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.
268
269 .. method:: get(key)
270
271 Returns the value for the cache key. If the item does not
272 exist in the cache the return value must be `None`.
273
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.
277
278 This bytecode cache does not support clearing of used items in the cache.
279 The clear method is a no-operation function.
280
281 .. versionadded:: 2.7
282 Added support for ignoring memcache errors through the
283 `ignore_memcache_errors` parameter.
284 """
285
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
292
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)
302
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
OLDNEW
« no previous file with comments | « mojo/public/third_party/jinja2/_stringdefs.py ('k') | mojo/public/third_party/jinja2/compiler.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698