OLD | NEW |
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 """ | 2 """ |
3 jinja2.bccache | 3 jinja2.bccache |
4 ~~~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~~~ |
5 | 5 |
6 This module implements the bytecode cache system Jinja is optionally | 6 This module implements the bytecode cache system Jinja is optionally |
7 using. This is useful if you have very complex template situations and | 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 | 8 the compiliation of all those templates slow down your application too |
9 much. | 9 much. |
10 | 10 |
11 Situations where this is useful are often forking web applications that | 11 Situations where this is useful are often forking web applications that |
12 are initialized on the first request. | 12 are initialized on the first request. |
13 | 13 |
14 :copyright: (c) 2010 by the Jinja Team. | 14 :copyright: (c) 2010 by the Jinja Team. |
15 :license: BSD. | 15 :license: BSD. |
16 """ | 16 """ |
17 from os import path, listdir | 17 from os import path, listdir |
| 18 import os |
18 import sys | 19 import sys |
| 20 import stat |
| 21 import errno |
19 import marshal | 22 import marshal |
20 import tempfile | 23 import tempfile |
21 import fnmatch | 24 import fnmatch |
22 from hashlib import sha1 | 25 from hashlib import sha1 |
23 from jinja2.utils import open_if_exists | 26 from jinja2.utils import open_if_exists |
24 from jinja2._compat import BytesIO, pickle, PY2, text_type | 27 from jinja2._compat import BytesIO, pickle, PY2, text_type |
25 | 28 |
26 | 29 |
27 # marshal works better on 3.x, one hack less required | 30 # marshal works better on 3.x, one hack less required |
28 if not PY2: | 31 if not PY2: |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 # make sure the magic header is correct | 81 # make sure the magic header is correct |
79 magic = f.read(len(bc_magic)) | 82 magic = f.read(len(bc_magic)) |
80 if magic != bc_magic: | 83 if magic != bc_magic: |
81 self.reset() | 84 self.reset() |
82 return | 85 return |
83 # the source code of the file changed, we need to reload | 86 # the source code of the file changed, we need to reload |
84 checksum = pickle.load(f) | 87 checksum = pickle.load(f) |
85 if self.checksum != checksum: | 88 if self.checksum != checksum: |
86 self.reset() | 89 self.reset() |
87 return | 90 return |
88 self.code = marshal_load(f) | 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 |
89 | 97 |
90 def write_bytecode(self, f): | 98 def write_bytecode(self, f): |
91 """Dump the bytecode into the file or file like object passed.""" | 99 """Dump the bytecode into the file or file like object passed.""" |
92 if self.code is None: | 100 if self.code is None: |
93 raise TypeError('can\'t write empty bucket') | 101 raise TypeError('can\'t write empty bucket') |
94 f.write(bc_magic) | 102 f.write(bc_magic) |
95 pickle.dump(self.checksum, f, 2) | 103 pickle.dump(self.checksum, f, 2) |
96 marshal_dump(self.code, f) | 104 marshal_dump(self.code, f) |
97 | 105 |
98 def bytecode_from_string(self, string): | 106 def bytecode_from_string(self, string): |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 def set_bucket(self, bucket): | 190 def set_bucket(self, bucket): |
183 """Put the bucket into the cache.""" | 191 """Put the bucket into the cache.""" |
184 self.dump_bytecode(bucket) | 192 self.dump_bytecode(bucket) |
185 | 193 |
186 | 194 |
187 class FileSystemBytecodeCache(BytecodeCache): | 195 class FileSystemBytecodeCache(BytecodeCache): |
188 """A bytecode cache that stores bytecode on the filesystem. It accepts | 196 """A bytecode cache that stores bytecode on the filesystem. It accepts |
189 two arguments: The directory where the cache items are stored and a | 197 two arguments: The directory where the cache items are stored and a |
190 pattern string that is used to build the filename. | 198 pattern string that is used to build the filename. |
191 | 199 |
192 If no directory is specified the system temporary items folder is used. | 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. |
193 | 203 |
194 The pattern can be used to have multiple separate caches operate on the | 204 The pattern can be used to have multiple separate caches operate on the |
195 same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` | 205 same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` |
196 is replaced with the cache key. | 206 is replaced with the cache key. |
197 | 207 |
198 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') | 208 >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') |
199 | 209 |
200 This bytecode cache supports clearing of the cache using the clear method. | 210 This bytecode cache supports clearing of the cache using the clear method. |
201 """ | 211 """ |
202 | 212 |
203 def __init__(self, directory=None, pattern='__jinja2_%s.cache'): | 213 def __init__(self, directory=None, pattern='__jinja2_%s.cache'): |
204 if directory is None: | 214 if directory is None: |
205 directory = tempfile.gettempdir() | 215 directory = self._get_default_cache_dir() |
206 self.directory = directory | 216 self.directory = directory |
207 self.pattern = pattern | 217 self.pattern = pattern |
208 | 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 |
209 def _get_cache_filename(self, bucket): | 260 def _get_cache_filename(self, bucket): |
210 return path.join(self.directory, self.pattern % bucket.key) | 261 return path.join(self.directory, self.pattern % bucket.key) |
211 | 262 |
212 def load_bytecode(self, bucket): | 263 def load_bytecode(self, bucket): |
213 f = open_if_exists(self._get_cache_filename(bucket), 'rb') | 264 f = open_if_exists(self._get_cache_filename(bucket), 'rb') |
214 if f is not None: | 265 if f is not None: |
215 try: | 266 try: |
216 bucket.load_bytecode(f) | 267 bucket.load_bytecode(f) |
217 finally: | 268 finally: |
218 f.close() | 269 f.close() |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
302 | 353 |
303 def dump_bytecode(self, bucket): | 354 def dump_bytecode(self, bucket): |
304 args = (self.prefix + bucket.key, bucket.bytecode_to_string()) | 355 args = (self.prefix + bucket.key, bucket.bytecode_to_string()) |
305 if self.timeout is not None: | 356 if self.timeout is not None: |
306 args += (self.timeout,) | 357 args += (self.timeout,) |
307 try: | 358 try: |
308 self.client.set(*args) | 359 self.client.set(*args) |
309 except Exception: | 360 except Exception: |
310 if not self.ignore_memcache_errors: | 361 if not self.ignore_memcache_errors: |
311 raise | 362 raise |
OLD | NEW |