Mercurial > repos > bcclaywell > argo_navis
comparison venv/lib/python2.7/site-packages/jinja2/bccache.py @ 0:d67268158946 draft
planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author | bcclaywell |
---|---|
date | Mon, 12 Oct 2015 17:43:33 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d67268158946 |
---|---|
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 os | |
19 import sys | |
20 import stat | |
21 import errno | |
22 import marshal | |
23 import tempfile | |
24 import fnmatch | |
25 from hashlib import sha1 | |
26 from jinja2.utils import open_if_exists | |
27 from jinja2._compat import BytesIO, pickle, PY2, text_type | |
28 | |
29 | |
30 # marshal works better on 3.x, one hack less required | |
31 if not PY2: | |
32 marshal_dump = marshal.dump | |
33 marshal_load = marshal.load | |
34 else: | |
35 | |
36 def marshal_dump(code, f): | |
37 if isinstance(f, file): | |
38 marshal.dump(code, f) | |
39 else: | |
40 f.write(marshal.dumps(code)) | |
41 | |
42 def marshal_load(f): | |
43 if isinstance(f, file): | |
44 return marshal.load(f) | |
45 return marshal.loads(f.read()) | |
46 | |
47 | |
48 bc_version = 2 | |
49 | |
50 # magic version used to only change with new jinja versions. With 2.6 | |
51 # we change this to also take Python version changes into account. The | |
52 # reason for this is that Python tends to segfault if fed earlier bytecode | |
53 # versions because someone thought it would be a good idea to reuse opcodes | |
54 # or make Python incompatible with earlier versions. | |
55 bc_magic = 'j2'.encode('ascii') + \ | |
56 pickle.dumps(bc_version, 2) + \ | |
57 pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1]) | |
58 | |
59 | |
60 class Bucket(object): | |
61 """Buckets are used to store the bytecode for one template. It's created | |
62 and initialized by the bytecode cache and passed to the loading functions. | |
63 | |
64 The buckets get an internal checksum from the cache assigned and use this | |
65 to automatically reject outdated cache material. Individual bytecode | |
66 cache subclasses don't have to care about cache invalidation. | |
67 """ | |
68 | |
69 def __init__(self, environment, key, checksum): | |
70 self.environment = environment | |
71 self.key = key | |
72 self.checksum = checksum | |
73 self.reset() | |
74 | |
75 def reset(self): | |
76 """Resets the bucket (unloads the bytecode).""" | |
77 self.code = None | |
78 | |
79 def load_bytecode(self, f): | |
80 """Loads bytecode from a file or file like object.""" | |
81 # make sure the magic header is correct | |
82 magic = f.read(len(bc_magic)) | |
83 if magic != bc_magic: | |
84 self.reset() | |
85 return | |
86 # the source code of the file changed, we need to reload | |
87 checksum = pickle.load(f) | |
88 if self.checksum != checksum: | |
89 self.reset() | |
90 return | |
91 # if marshal_load fails then we need to reload | |
92 try: | |
93 self.code = marshal_load(f) | |
94 except (EOFError, ValueError, TypeError): | |
95 self.reset() | |
96 return | |
97 | |
98 def write_bytecode(self, f): | |
99 """Dump the bytecode into the file or file like object passed.""" | |
100 if self.code is None: | |
101 raise TypeError('can\'t write empty bucket') | |
102 f.write(bc_magic) | |
103 pickle.dump(self.checksum, f, 2) | |
104 marshal_dump(self.code, f) | |
105 | |
106 def bytecode_from_string(self, string): | |
107 """Load bytecode from a string.""" | |
108 self.load_bytecode(BytesIO(string)) | |
109 | |
110 def bytecode_to_string(self): | |
111 """Return the bytecode as string.""" | |
112 out = BytesIO() | |
113 self.write_bytecode(out) | |
114 return out.getvalue() | |
115 | |
116 | |
117 class BytecodeCache(object): | |
118 """To implement your own bytecode cache you have to subclass this class | |
119 and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of | |
120 these methods are passed a :class:`~jinja2.bccache.Bucket`. | |
121 | |
122 A very basic bytecode cache that saves the bytecode on the file system:: | |
123 | |
124 from os import path | |
125 | |
126 class MyCache(BytecodeCache): | |
127 | |
128 def __init__(self, directory): | |
129 self.directory = directory | |
130 | |
131 def load_bytecode(self, bucket): | |
132 filename = path.join(self.directory, bucket.key) | |
133 if path.exists(filename): | |
134 with open(filename, 'rb') as f: | |
135 bucket.load_bytecode(f) | |
136 | |
137 def dump_bytecode(self, bucket): | |
138 filename = path.join(self.directory, bucket.key) | |
139 with open(filename, 'wb') as f: | |
140 bucket.write_bytecode(f) | |
141 | |
142 A more advanced version of a filesystem based bytecode cache is part of | |
143 Jinja2. | |
144 """ | |
145 | |
146 def load_bytecode(self, bucket): | |
147 """Subclasses have to override this method to load bytecode into a | |
148 bucket. If they are not able to find code in the cache for the | |
149 bucket, it must not do anything. | |
150 """ | |
151 raise NotImplementedError() | |
152 | |
153 def dump_bytecode(self, bucket): | |
154 """Subclasses have to override this method to write the bytecode | |
155 from a bucket back to the cache. If it unable to do so it must not | |
156 fail silently but raise an exception. | |
157 """ | |
158 raise NotImplementedError() | |
159 | |
160 def clear(self): | |
161 """Clears the cache. This method is not used by Jinja2 but should be | |
162 implemented to allow applications to clear the bytecode cache used | |
163 by a particular environment. | |
164 """ | |
165 | |
166 def get_cache_key(self, name, filename=None): | |
167 """Returns the unique hash key for this template name.""" | |
168 hash = sha1(name.encode('utf-8')) | |
169 if filename is not None: | |
170 filename = '|' + filename | |
171 if isinstance(filename, text_type): | |
172 filename = filename.encode('utf-8') | |
173 hash.update(filename) | |
174 return hash.hexdigest() | |
175 | |
176 def get_source_checksum(self, source): | |
177 """Returns a checksum for the source.""" | |
178 return sha1(source.encode('utf-8')).hexdigest() | |
179 | |
180 def get_bucket(self, environment, name, filename, source): | |
181 """Return a cache bucket for the given template. All arguments are | |
182 mandatory but filename may be `None`. | |
183 """ | |
184 key = self.get_cache_key(name, filename) | |
185 checksum = self.get_source_checksum(source) | |
186 bucket = Bucket(environment, key, checksum) | |
187 self.load_bytecode(bucket) | |
188 return bucket | |
189 | |
190 def set_bucket(self, bucket): | |
191 """Put the bucket into the cache.""" | |
192 self.dump_bytecode(bucket) | |
193 | |
194 | |
195 class FileSystemBytecodeCache(BytecodeCache): | |
196 """A bytecode cache that stores bytecode on the filesystem. It accepts | |
197 two arguments: The directory where the cache items are stored and a | |
198 pattern string that is used to build the filename. | |
199 | |
200 If no directory is specified a default cache directory is selected. On | |
201 Windows the user's temp directory is used, on UNIX systems a directory | |
202 is created for the user in the system temp directory. | |
203 | |
204 The pattern can be used to have multiple separate caches operate on the | |
205 same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` | |
206 is replaced with the cache key. | |
207 | |
208 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') | |
209 | |
210 This bytecode cache supports clearing of the cache using the clear method. | |
211 """ | |
212 | |
213 def __init__(self, directory=None, pattern='__jinja2_%s.cache'): | |
214 if directory is None: | |
215 directory = self._get_default_cache_dir() | |
216 self.directory = directory | |
217 self.pattern = pattern | |
218 | |
219 def _get_default_cache_dir(self): | |
220 def _unsafe_dir(): | |
221 raise RuntimeError('Cannot determine safe temp directory. You ' | |
222 'need to explicitly provide one.') | |
223 | |
224 tmpdir = tempfile.gettempdir() | |
225 | |
226 # On windows the temporary directory is used specific unless | |
227 # explicitly forced otherwise. We can just use that. | |
228 if os.name == 'nt': | |
229 return tmpdir | |
230 if not hasattr(os, 'getuid'): | |
231 _unsafe_dir() | |
232 | |
233 dirname = '_jinja2-cache-%d' % os.getuid() | |
234 actual_dir = os.path.join(tmpdir, dirname) | |
235 | |
236 try: | |
237 os.mkdir(actual_dir, stat.S_IRWXU) | |
238 except OSError as e: | |
239 if e.errno != errno.EEXIST: | |
240 raise | |
241 try: | |
242 os.chmod(actual_dir, stat.S_IRWXU) | |
243 actual_dir_stat = os.lstat(actual_dir) | |
244 if actual_dir_stat.st_uid != os.getuid() \ | |
245 or not stat.S_ISDIR(actual_dir_stat.st_mode) \ | |
246 or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: | |
247 _unsafe_dir() | |
248 except OSError as e: | |
249 if e.errno != errno.EEXIST: | |
250 raise | |
251 | |
252 actual_dir_stat = os.lstat(actual_dir) | |
253 if actual_dir_stat.st_uid != os.getuid() \ | |
254 or not stat.S_ISDIR(actual_dir_stat.st_mode) \ | |
255 or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: | |
256 _unsafe_dir() | |
257 | |
258 return actual_dir | |
259 | |
260 def _get_cache_filename(self, bucket): | |
261 return path.join(self.directory, self.pattern % bucket.key) | |
262 | |
263 def load_bytecode(self, bucket): | |
264 f = open_if_exists(self._get_cache_filename(bucket), 'rb') | |
265 if f is not None: | |
266 try: | |
267 bucket.load_bytecode(f) | |
268 finally: | |
269 f.close() | |
270 | |
271 def dump_bytecode(self, bucket): | |
272 f = open(self._get_cache_filename(bucket), 'wb') | |
273 try: | |
274 bucket.write_bytecode(f) | |
275 finally: | |
276 f.close() | |
277 | |
278 def clear(self): | |
279 # imported lazily here because google app-engine doesn't support | |
280 # write access on the file system and the function does not exist | |
281 # normally. | |
282 from os import remove | |
283 files = fnmatch.filter(listdir(self.directory), self.pattern % '*') | |
284 for filename in files: | |
285 try: | |
286 remove(path.join(self.directory, filename)) | |
287 except OSError: | |
288 pass | |
289 | |
290 | |
291 class MemcachedBytecodeCache(BytecodeCache): | |
292 """This class implements a bytecode cache that uses a memcache cache for | |
293 storing the information. It does not enforce a specific memcache library | |
294 (tummy's memcache or cmemcache) but will accept any class that provides | |
295 the minimal interface required. | |
296 | |
297 Libraries compatible with this class: | |
298 | |
299 - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache | |
300 - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_ | |
301 - `cmemcache <http://gijsbert.org/cmemcache/>`_ | |
302 | |
303 (Unfortunately the django cache interface is not compatible because it | |
304 does not support storing binary data, only unicode. You can however pass | |
305 the underlying cache client to the bytecode cache which is available | |
306 as `django.core.cache.cache._client`.) | |
307 | |
308 The minimal interface for the client passed to the constructor is this: | |
309 | |
310 .. class:: MinimalClientInterface | |
311 | |
312 .. method:: set(key, value[, timeout]) | |
313 | |
314 Stores the bytecode in the cache. `value` is a string and | |
315 `timeout` the timeout of the key. If timeout is not provided | |
316 a default timeout or no timeout should be assumed, if it's | |
317 provided it's an integer with the number of seconds the cache | |
318 item should exist. | |
319 | |
320 .. method:: get(key) | |
321 | |
322 Returns the value for the cache key. If the item does not | |
323 exist in the cache the return value must be `None`. | |
324 | |
325 The other arguments to the constructor are the prefix for all keys that | |
326 is added before the actual cache key and the timeout for the bytecode in | |
327 the cache system. We recommend a high (or no) timeout. | |
328 | |
329 This bytecode cache does not support clearing of used items in the cache. | |
330 The clear method is a no-operation function. | |
331 | |
332 .. versionadded:: 2.7 | |
333 Added support for ignoring memcache errors through the | |
334 `ignore_memcache_errors` parameter. | |
335 """ | |
336 | |
337 def __init__(self, client, prefix='jinja2/bytecode/', timeout=None, | |
338 ignore_memcache_errors=True): | |
339 self.client = client | |
340 self.prefix = prefix | |
341 self.timeout = timeout | |
342 self.ignore_memcache_errors = ignore_memcache_errors | |
343 | |
344 def load_bytecode(self, bucket): | |
345 try: | |
346 code = self.client.get(self.prefix + bucket.key) | |
347 except Exception: | |
348 if not self.ignore_memcache_errors: | |
349 raise | |
350 code = None | |
351 if code is not None: | |
352 bucket.bytecode_from_string(code) | |
353 | |
354 def dump_bytecode(self, bucket): | |
355 args = (self.prefix + bucket.key, bucket.bytecode_to_string()) | |
356 if self.timeout is not None: | |
357 args += (self.timeout,) | |
358 try: | |
359 self.client.set(*args) | |
360 except Exception: | |
361 if not self.ignore_memcache_errors: | |
362 raise |