diff options
| author | floatdrop <[email protected]> | 2022-02-10 16:47:15 +0300 |
|---|---|---|
| committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:47:15 +0300 |
| commit | e63b84f1d39557d9e46ac380b1f388271894293c (patch) | |
| tree | 338cdaff3fb027e030b847db66df06019a0e3149 /contrib/python/Jinja2/py3/jinja2/bccache.py | |
| parent | f60febb7ea449535e7b073c386c7ff0539637fc0 (diff) | |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/Jinja2/py3/jinja2/bccache.py')
| -rw-r--r-- | contrib/python/Jinja2/py3/jinja2/bccache.py | 480 |
1 files changed, 240 insertions, 240 deletions
diff --git a/contrib/python/Jinja2/py3/jinja2/bccache.py b/contrib/python/Jinja2/py3/jinja2/bccache.py index 3bb61b7c34c..f54cc4d3b63 100644 --- a/contrib/python/Jinja2/py3/jinja2/bccache.py +++ b/contrib/python/Jinja2/py3/jinja2/bccache.py @@ -1,27 +1,27 @@ """The optional bytecode cache system. This is useful if you have very complex template situations and the compilation of all those templates slows down your application too much. - + Situations where this is useful are often forking web applications that are initialized on the first request. -""" +""" import errno import fnmatch import marshal -import os +import os import pickle import stat -import sys -import tempfile +import sys +import tempfile import typing as t -from hashlib import sha1 +from hashlib import sha1 from io import BytesIO from types import CodeType - + if t.TYPE_CHECKING: import typing_extensions as te from .environment import Environment - + class _MemcachedClient(te.Protocol): def get(self, key: str) -> bytes: ... @@ -39,129 +39,129 @@ bc_magic = ( + pickle.dumps(bc_version, 2) + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) ) - - + + class Bucket: - """Buckets are used to store the bytecode for one template. It's created - and initialized by the bytecode cache and passed to the loading functions. - - The buckets get an internal checksum from the cache assigned and use this - to automatically reject outdated cache material. Individual bytecode - cache subclasses don't have to care about cache invalidation. - """ - + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: - self.environment = environment - self.key = key - self.checksum = checksum - self.reset() - + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + def reset(self) -> None: - """Resets the bucket (unloads the bytecode).""" + """Resets the bucket (unloads the bytecode).""" self.code: t.Optional[CodeType] = None - + def load_bytecode(self, f: t.BinaryIO) -> None: - """Loads bytecode from a file or file like object.""" - # make sure the magic header is correct - magic = f.read(len(bc_magic)) - if magic != bc_magic: - self.reset() - return - # the source code of the file changed, we need to reload - checksum = pickle.load(f) - if self.checksum != checksum: - self.reset() - return - # if marshal_load fails then we need to reload - try: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: self.code = marshal.load(f) - except (EOFError, ValueError, TypeError): - self.reset() - return - + except (EOFError, ValueError, TypeError): + self.reset() + return + def write_bytecode(self, f: t.BinaryIO) -> None: - """Dump the bytecode into the file or file like object passed.""" - if self.code is None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: raise TypeError("can't write empty bucket") - f.write(bc_magic) - pickle.dump(self.checksum, f, 2) + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) marshal.dump(self.code, f) - + def bytecode_from_string(self, string: bytes) -> None: """Load bytecode from bytes.""" - self.load_bytecode(BytesIO(string)) - + self.load_bytecode(BytesIO(string)) + def bytecode_to_string(self) -> bytes: """Return the bytecode as bytes.""" - out = BytesIO() - self.write_bytecode(out) - return out.getvalue() - - + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + class BytecodeCache: - """To implement your own bytecode cache you have to subclass this class - and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of - these methods are passed a :class:`~jinja2.bccache.Bucket`. - - A very basic bytecode cache that saves the bytecode on the file system:: - - from os import path - - class MyCache(BytecodeCache): - - def __init__(self, directory): - self.directory = directory - - def load_bytecode(self, bucket): - filename = path.join(self.directory, bucket.key) - if path.exists(filename): - with open(filename, 'rb') as f: - bucket.load_bytecode(f) - - def dump_bytecode(self, bucket): - filename = path.join(self.directory, bucket.key) - with open(filename, 'wb') as f: - bucket.write_bytecode(f) - - A more advanced version of a filesystem based bytecode cache is part of + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of Jinja. - """ - + """ + def load_bytecode(self, bucket: Bucket) -> None: - """Subclasses have to override this method to load bytecode into a - bucket. If they are not able to find code in the cache for the - bucket, it must not do anything. - """ - raise NotImplementedError() - + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + def dump_bytecode(self, bucket: Bucket) -> None: - """Subclasses have to override this method to write the bytecode - from a bucket back to the cache. If it unable to do so it must not - fail silently but raise an exception. - """ - raise NotImplementedError() - + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + def clear(self) -> None: """Clears the cache. This method is not used by Jinja but should be - implemented to allow applications to clear the bytecode cache used - by a particular environment. - """ - + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + def get_cache_key( self, name: str, filename: t.Optional[t.Union[str]] = None ) -> str: - """Returns the unique hash key for this template name.""" + """Returns the unique hash key for this template name.""" hash = sha1(name.encode("utf-8")) - if filename is not None: + if filename is not None: hash.update(f"|{filename}".encode()) - return hash.hexdigest() - + return hash.hexdigest() + def get_source_checksum(self, source: str) -> str: - """Returns a checksum for the source.""" + """Returns a checksum for the source.""" return sha1(source.encode("utf-8")).hexdigest() - + def get_bucket( self, environment: "Environment", @@ -169,166 +169,166 @@ class BytecodeCache: filename: t.Optional[str], source: str, ) -> Bucket: - """Return a cache bucket for the given template. All arguments are - mandatory but filename may be `None`. - """ - key = self.get_cache_key(name, filename) - checksum = self.get_source_checksum(source) - bucket = Bucket(environment, key, checksum) - self.load_bytecode(bucket) - return bucket - + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + def set_bucket(self, bucket: Bucket) -> None: - """Put the bucket into the cache.""" - self.dump_bytecode(bucket) - - -class FileSystemBytecodeCache(BytecodeCache): - """A bytecode cache that stores bytecode on the filesystem. It accepts - two arguments: The directory where the cache items are stored and a - pattern string that is used to build the filename. - - If no directory is specified a default cache directory is selected. On - Windows the user's temp directory is used, on UNIX systems a directory - is created for the user in the system temp directory. - - The pattern can be used to have multiple separate caches operate on the - same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` - is replaced with the cache key. - - >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') - - This bytecode cache supports clearing of the cache using the clear method. - """ - + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + def __init__( self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" ) -> None: - if directory is None: - directory = self._get_default_cache_dir() - self.directory = directory - self.pattern = pattern - + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + def _get_default_cache_dir(self) -> str: def _unsafe_dir() -> "te.NoReturn": raise RuntimeError( "Cannot determine safe temp directory. You " "need to explicitly provide one." ) - - tmpdir = tempfile.gettempdir() - - # On windows the temporary directory is used specific unless - # explicitly forced otherwise. We can just use that. + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. if os.name == "nt": - return tmpdir + return tmpdir if not hasattr(os, "getuid"): - _unsafe_dir() - + _unsafe_dir() + dirname = f"_jinja2-cache-{os.getuid()}" - actual_dir = os.path.join(tmpdir, dirname) - - try: - os.mkdir(actual_dir, stat.S_IRWXU) - except OSError as e: - if e.errno != errno.EEXIST: - raise - try: - os.chmod(actual_dir, stat.S_IRWXU) - actual_dir_stat = os.lstat(actual_dir) + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) if ( actual_dir_stat.st_uid != os.getuid() or not stat.S_ISDIR(actual_dir_stat.st_mode) or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU ): - _unsafe_dir() - except OSError as e: - if e.errno != errno.EEXIST: - raise - - actual_dir_stat = os.lstat(actual_dir) + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) if ( actual_dir_stat.st_uid != os.getuid() or not stat.S_ISDIR(actual_dir_stat.st_mode) or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU ): - _unsafe_dir() - - return actual_dir - + _unsafe_dir() + + return actual_dir + def _get_cache_filename(self, bucket: Bucket) -> str: return os.path.join(self.directory, self.pattern % (bucket.key,)) - + def load_bytecode(self, bucket: Bucket) -> None: filename = self._get_cache_filename(bucket) if os.path.exists(filename): with open(filename, "rb") as f: - bucket.load_bytecode(f) - + bucket.load_bytecode(f) + def dump_bytecode(self, bucket: Bucket) -> None: with open(self._get_cache_filename(bucket), "wb") as f: - bucket.write_bytecode(f) - + bucket.write_bytecode(f) + def clear(self) -> None: - # imported lazily here because google app-engine doesn't support - # write access on the file system and the function does not exist - # normally. - from os import remove + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) - for filename in files: - try: + for filename in files: + try: remove(os.path.join(self.directory, filename)) - except OSError: - pass - - -class MemcachedBytecodeCache(BytecodeCache): - """This class implements a bytecode cache that uses a memcache cache for - storing the information. It does not enforce a specific memcache library - (tummy's memcache or cmemcache) but will accept any class that provides - the minimal interface required. - - Libraries compatible with this class: - + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + - `cachelib <https://github.com/pallets/cachelib>`_ - `python-memcached <https://pypi.org/project/python-memcached/>`_ - - (Unfortunately the django cache interface is not compatible because it + + (Unfortunately the django cache interface is not compatible because it does not support storing binary data, only text. You can however pass - the underlying cache client to the bytecode cache which is available - as `django.core.cache.cache._client`.) - - The minimal interface for the client passed to the constructor is this: - - .. class:: MinimalClientInterface - - .. method:: set(key, value[, timeout]) - - Stores the bytecode in the cache. `value` is a string and - `timeout` the timeout of the key. If timeout is not provided - a default timeout or no timeout should be assumed, if it's - provided it's an integer with the number of seconds the cache - item should exist. - - .. method:: get(key) - - Returns the value for the cache key. If the item does not - exist in the cache the return value must be `None`. - - The other arguments to the constructor are the prefix for all keys that - is added before the actual cache key and the timeout for the bytecode in - the cache system. We recommend a high (or no) timeout. - - This bytecode cache does not support clearing of used items in the cache. - The clear method is a no-operation function. - - .. versionadded:: 2.7 - Added support for ignoring memcache errors through the - `ignore_memcache_errors` parameter. - """ - + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + def __init__( self, client: "_MemcachedClient", @@ -336,29 +336,29 @@ class MemcachedBytecodeCache(BytecodeCache): timeout: t.Optional[int] = None, ignore_memcache_errors: bool = True, ): - self.client = client - self.prefix = prefix - self.timeout = timeout - self.ignore_memcache_errors = ignore_memcache_errors - + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + def load_bytecode(self, bucket: Bucket) -> None: - try: - code = self.client.get(self.prefix + bucket.key) - except Exception: - if not self.ignore_memcache_errors: - raise + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise else: - bucket.bytecode_from_string(code) - + bucket.bytecode_from_string(code) + def dump_bytecode(self, bucket: Bucket) -> None: key = self.prefix + bucket.key value = bucket.bytecode_to_string() - try: + try: if self.timeout is not None: self.client.set(key, value, self.timeout) else: self.client.set(key, value) - except Exception: - if not self.ignore_memcache_errors: - raise + except Exception: + if not self.ignore_memcache_errors: + raise |
