diff options
author | floatdrop <floatdrop@yandex-team.ru> | 2022-02-10 16:47:15 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:15 +0300 |
commit | 4267de875ca703ff841f2e025723dadc78f3cc02 (patch) | |
tree | 9814fbd1c3effac9b8377c5d604b367b14e2db55 /contrib/python/Jinja2/py3/jinja2/loaders.py | |
parent | e63b84f1d39557d9e46ac380b1f388271894293c (diff) | |
download | ydb-4267de875ca703ff841f2e025723dadc78f3cc02.tar.gz |
Restoring authorship annotation for <floatdrop@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/Jinja2/py3/jinja2/loaders.py')
-rw-r--r-- | contrib/python/Jinja2/py3/jinja2/loaders.py | 680 |
1 files changed, 340 insertions, 340 deletions
diff --git a/contrib/python/Jinja2/py3/jinja2/loaders.py b/contrib/python/Jinja2/py3/jinja2/loaders.py index c4b35e0325..cc58eca221 100644 --- a/contrib/python/Jinja2/py3/jinja2/loaders.py +++ b/contrib/python/Jinja2/py3/jinja2/loaders.py @@ -1,22 +1,22 @@ """API and implementations for loading templates from different data sources. -""" +""" import importlib.util -import os -import sys +import os +import sys import pkgutil import typing as t -import weakref +import weakref import zipimport from collections import abc from hashlib import sha1 from importlib import import_module -from types import ModuleType - +from types import ModuleType + from .exceptions import TemplateNotFound from .utils import internalcode from .utils import open_if_exists - + if t.TYPE_CHECKING: from .environment import Environment from .environment import Template @@ -25,147 +25,147 @@ import __res as arcadia_res def split_template_path(template: str) -> t.List[str]: - """Split a path into segments and perform a sanity check. If it detects - '..' in the path it will raise a `TemplateNotFound` error. - """ - pieces = [] + """Split a path into segments and perform a sanity check. If it detects + '..' in the path it will raise a `TemplateNotFound` error. + """ + pieces = [] for piece in template.split("/"): if ( os.path.sep in piece or (os.path.altsep and os.path.altsep in piece) or piece == os.path.pardir ): - raise TemplateNotFound(template) + raise TemplateNotFound(template) elif piece and piece != ".": - pieces.append(piece) - return pieces - - + pieces.append(piece) + return pieces + + class BaseLoader: - """Baseclass for all loaders. Subclass this and override `get_source` to - implement a custom loading mechanism. The environment provides a - `get_template` method that calls the loader's `load` method to get the - :class:`Template` object. - - A very basic example for a loader that looks up templates on the file - system could look like this:: - - from jinja2 import BaseLoader, TemplateNotFound - from os.path import join, exists, getmtime - - class MyLoader(BaseLoader): - - def __init__(self, path): - self.path = path - - def get_source(self, environment, template): - path = join(self.path, template) - if not exists(path): - raise TemplateNotFound(template) - mtime = getmtime(path) + """Baseclass for all loaders. Subclass this and override `get_source` to + implement a custom loading mechanism. The environment provides a + `get_template` method that calls the loader's `load` method to get the + :class:`Template` object. + + A very basic example for a loader that looks up templates on the file + system could look like this:: + + from jinja2 import BaseLoader, TemplateNotFound + from os.path import join, exists, getmtime + + class MyLoader(BaseLoader): + + def __init__(self, path): + self.path = path + + def get_source(self, environment, template): + path = join(self.path, template) + if not exists(path): + raise TemplateNotFound(template) + mtime = getmtime(path) with open(path) as f: source = f.read() - return source, path, lambda: mtime == getmtime(path) - """ - - #: if set to `False` it indicates that the loader cannot provide access - #: to the source of templates. - #: - #: .. versionadded:: 2.4 - has_source_access = True - + return source, path, lambda: mtime == getmtime(path) + """ + + #: if set to `False` it indicates that the loader cannot provide access + #: to the source of templates. + #: + #: .. versionadded:: 2.4 + has_source_access = True + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]: - """Get the template source, filename and reload helper for a template. - It's passed the environment and template name and has to return a - tuple in the form ``(source, filename, uptodate)`` or raise a - `TemplateNotFound` error if it can't locate the template. - - The source part of the returned tuple must be the source of the + """Get the template source, filename and reload helper for a template. + It's passed the environment and template name and has to return a + tuple in the form ``(source, filename, uptodate)`` or raise a + `TemplateNotFound` error if it can't locate the template. + + The source part of the returned tuple must be the source of the template as a string. The filename should be the name of the file on the filesystem if it was loaded from there, otherwise ``None``. The filename is used by Python for the tracebacks - if no loader extension is used. - - The last item in the tuple is the `uptodate` function. If auto - reloading is enabled it's always called to check if the template - changed. No arguments are passed so the function must store the - old state somewhere (for example in a closure). If it returns `False` - the template will be reloaded. - """ - if not self.has_source_access: + if no loader extension is used. + + The last item in the tuple is the `uptodate` function. If auto + reloading is enabled it's always called to check if the template + changed. No arguments are passed so the function must store the + old state somewhere (for example in a closure). If it returns `False` + the template will be reloaded. + """ + if not self.has_source_access: raise RuntimeError( f"{type(self).__name__} cannot provide access to the source" ) - raise TemplateNotFound(template) - + raise TemplateNotFound(template) + def list_templates(self) -> t.List[str]: - """Iterates over all templates. If the loader does not support that - it should raise a :exc:`TypeError` which is the default behavior. - """ + """Iterates over all templates. If the loader does not support that + it should raise a :exc:`TypeError` which is the default behavior. + """ raise TypeError("this loader cannot iterate over all templates") - - @internalcode + + @internalcode def load( self, environment: "Environment", name: str, globals: t.Optional[t.MutableMapping[str, t.Any]] = None, ) -> "Template": - """Loads a template. This method looks up the template in the cache - or loads one by calling :meth:`get_source`. Subclasses should not - override this method as loaders working on collections of other - loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) - will not call this method but `get_source` directly. - """ - code = None - if globals is None: - globals = {} - - # first we try to get the source for this template together - # with the filename and the uptodate function. - source, filename, uptodate = self.get_source(environment, name) - - # try to load the code from the bytecode cache if there is a - # bytecode cache configured. - bcc = environment.bytecode_cache - if bcc is not None: - bucket = bcc.get_bucket(environment, name, filename, source) - code = bucket.code - - # if we don't have code so far (not cached, no longer up to - # date) etc. we compile the template - if code is None: - code = environment.compile(source, name, filename) - - # if the bytecode cache is available and the bucket doesn't - # have a code so far, we give the bucket the new code and put - # it back to the bytecode cache. - if bcc is not None and bucket.code is None: - bucket.code = code - bcc.set_bucket(bucket) - + """Loads a template. This method looks up the template in the cache + or loads one by calling :meth:`get_source`. Subclasses should not + override this method as loaders working on collections of other + loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) + will not call this method but `get_source` directly. + """ + code = None + if globals is None: + globals = {} + + # first we try to get the source for this template together + # with the filename and the uptodate function. + source, filename, uptodate = self.get_source(environment, name) + + # try to load the code from the bytecode cache if there is a + # bytecode cache configured. + bcc = environment.bytecode_cache + if bcc is not None: + bucket = bcc.get_bucket(environment, name, filename, source) + code = bucket.code + + # if we don't have code so far (not cached, no longer up to + # date) etc. we compile the template + if code is None: + code = environment.compile(source, name, filename) + + # if the bytecode cache is available and the bucket doesn't + # have a code so far, we give the bucket the new code and put + # it back to the bytecode cache. + if bcc is not None and bucket.code is None: + bucket.code = code + bcc.set_bucket(bucket) + return environment.template_class.from_code( environment, code, globals, uptodate ) - - -class FileSystemLoader(BaseLoader): + + +class FileSystemLoader(BaseLoader): """Load templates from a directory in the file system. - + The path can be relative or absolute. Relative paths are relative to the current working directory. - + .. code-block:: python - + loader = FileSystemLoader("templates") - + A list of paths can be given. The directories will be searched in order, stopping at the first matching template. - + .. code-block:: python - + loader = FileSystemLoader(["/override/templates", "/default/templates"]) :param searchpath: A path, or list of paths, to the directory that @@ -176,8 +176,8 @@ class FileSystemLoader(BaseLoader): .. versionchanged:: 2.8 Added the ``followlinks`` parameter. - """ - + """ + def __init__( self, searchpath: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]], @@ -185,67 +185,67 @@ class FileSystemLoader(BaseLoader): followlinks: bool = False, ) -> None: if not isinstance(searchpath, abc.Iterable) or isinstance(searchpath, str): - searchpath = [searchpath] + searchpath = [searchpath] self.searchpath = [os.fspath(p) for p in searchpath] - self.encoding = encoding - self.followlinks = followlinks - + self.encoding = encoding + self.followlinks = followlinks + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, str, t.Callable[[], bool]]: - pieces = split_template_path(template) - for searchpath in self.searchpath: + pieces = split_template_path(template) + for searchpath in self.searchpath: filename = os.path.join(searchpath, *pieces) - f = open_if_exists(filename) - if f is None: - continue - try: - contents = f.read().decode(self.encoding) - finally: - f.close() - + f = open_if_exists(filename) + if f is None: + continue + try: + contents = f.read().decode(self.encoding) + finally: + f.close() + mtime = os.path.getmtime(filename) - + def uptodate() -> bool: - try: + try: return os.path.getmtime(filename) == mtime - except OSError: - return False + except OSError: + return False + + return contents, filename, uptodate + raise TemplateNotFound(template) - return contents, filename, uptodate - raise TemplateNotFound(template) - def list_templates(self) -> t.List[str]: - found = set() - for searchpath in self.searchpath: - walk_dir = os.walk(searchpath, followlinks=self.followlinks) + found = set() + for searchpath in self.searchpath: + walk_dir = os.walk(searchpath, followlinks=self.followlinks) for dirpath, _, filenames in walk_dir: - for filename in filenames: + for filename in filenames: template = ( os.path.join(dirpath, filename)[len(searchpath) :] .strip(os.path.sep) .replace(os.path.sep, "/") ) if template[:2] == "./": - template = template[2:] - if template not in found: - found.add(template) - return sorted(found) - - -class PackageLoader(BaseLoader): + template = template[2:] + if template not in found: + found.add(template) + return sorted(found) + + +class PackageLoader(BaseLoader): """Load templates from a directory in a Python package. - + :param package_name: Import name of the package that contains the template directory. :param package_path: Directory within the imported package that contains the templates. :param encoding: Encoding of template files. - + The following example looks up templates in the ``pages`` directory within the ``project.ui`` package. - + .. code-block:: python loader = PackageLoader("project.ui", "pages") @@ -265,8 +265,8 @@ class PackageLoader(BaseLoader): .. versionchanged:: 3.0 Limited PEP 420 namespace package support. - """ - + """ + def __init__( self, package_name: str, @@ -283,8 +283,8 @@ class PackageLoader(BaseLoader): self.package_path = package_path self.package_name = package_name - self.encoding = encoding - + self.encoding = encoding + # Make sure the package exists. This also makes namespace # packages work, otherwise get_loader returns None. package = import_module(package_name) @@ -316,7 +316,7 @@ class PackageLoader(BaseLoader): for root in roots: root = os.path.join(root, package_path) - + if os.path.isdir(root): template_root = root break @@ -328,13 +328,13 @@ class PackageLoader(BaseLoader): ) self._template_root = template_root - + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, str, t.Optional[t.Callable[[], bool]]]: p = os.path.join(self._template_root, *split_template_path(template)) up_to_date: t.Optional[t.Callable[[], bool]] - + if self._archive is None and hasattr(self, "_package"): try: source = pkgutil.get_data(self.package_name, os.path.join(self.package_path, *split_template_path(template))) @@ -408,52 +408,52 @@ class PackageLoader(BaseLoader): if name.startswith(prefix) and name[-1] != os.path.sep: results.append(name[offset:].replace(os.path.sep, "/")) - results.sort() - return results - - -class DictLoader(BaseLoader): + results.sort() + return results + + +class DictLoader(BaseLoader): """Loads a template from a Python dict mapping template names to template source. This loader is useful for unittesting: - - >>> loader = DictLoader({'index.html': 'source here'}) - - Because auto reloading is rarely useful this is disabled per default. - """ - + + >>> loader = DictLoader({'index.html': 'source here'}) + + Because auto reloading is rarely useful this is disabled per default. + """ + def __init__(self, mapping: t.Mapping[str, str]) -> None: - self.mapping = mapping - + self.mapping = mapping + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, None, t.Callable[[], bool]]: - if template in self.mapping: - source = self.mapping[template] - return source, None, lambda: source == self.mapping.get(template) - raise TemplateNotFound(template) - + if template in self.mapping: + source = self.mapping[template] + return source, None, lambda: source == self.mapping.get(template) + raise TemplateNotFound(template) + def list_templates(self) -> t.List[str]: - return sorted(self.mapping) - - -class FunctionLoader(BaseLoader): - """A loader that is passed a function which does the loading. The - function receives the name of the template and has to return either + return sorted(self.mapping) + + +class FunctionLoader(BaseLoader): + """A loader that is passed a function which does the loading. The + function receives the name of the template and has to return either a string with the template source, a tuple in the form ``(source, - filename, uptodatefunc)`` or `None` if the template does not exist. - - >>> def load_template(name): - ... if name == 'index.html': - ... return '...' - ... - >>> loader = FunctionLoader(load_template) - - The `uptodatefunc` is a function that is called if autoreload is enabled - and has to return `True` if the template is still up to date. For more - details have a look at :meth:`BaseLoader.get_source` which has the same - return value. - """ - + filename, uptodatefunc)`` or `None` if the template does not exist. + + >>> def load_template(name): + ... if name == 'index.html': + ... return '...' + ... + >>> loader = FunctionLoader(load_template) + + The `uptodatefunc` is a function that is called if autoreload is enabled + and has to return `True` if the template is still up to date. For more + details have a look at :meth:`BaseLoader.get_source` which has the same + return value. + """ + def __init__( self, load_func: t.Callable[ @@ -465,222 +465,222 @@ class FunctionLoader(BaseLoader): ], ], ) -> None: - self.load_func = load_func - + self.load_func = load_func + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]: - rv = self.load_func(template) + rv = self.load_func(template) - if rv is None: - raise TemplateNotFound(template) + if rv is None: + raise TemplateNotFound(template) if isinstance(rv, str): - return rv, None, None - - return rv - - -class PrefixLoader(BaseLoader): - """A loader that is passed a dict of loaders where each loader is bound - to a prefix. The prefix is delimited from the template by a slash per - default, which can be changed by setting the `delimiter` argument to - something else:: - - loader = PrefixLoader({ - 'app1': PackageLoader('mypackage.app1'), - 'app2': PackageLoader('mypackage.app2') - }) - - By loading ``'app1/index.html'`` the file from the app1 package is loaded, - by loading ``'app2/index.html'`` the file from the second. - """ - + return rv, None, None + + return rv + + +class PrefixLoader(BaseLoader): + """A loader that is passed a dict of loaders where each loader is bound + to a prefix. The prefix is delimited from the template by a slash per + default, which can be changed by setting the `delimiter` argument to + something else:: + + loader = PrefixLoader({ + 'app1': PackageLoader('mypackage.app1'), + 'app2': PackageLoader('mypackage.app2') + }) + + By loading ``'app1/index.html'`` the file from the app1 package is loaded, + by loading ``'app2/index.html'`` the file from the second. + """ + def __init__( self, mapping: t.Mapping[str, BaseLoader], delimiter: str = "/" ) -> None: - self.mapping = mapping - self.delimiter = delimiter - + self.mapping = mapping + self.delimiter = delimiter + def get_loader(self, template: str) -> t.Tuple[BaseLoader, str]: - try: - prefix, name = template.split(self.delimiter, 1) - loader = self.mapping[prefix] + try: + prefix, name = template.split(self.delimiter, 1) + loader = self.mapping[prefix] except (ValueError, KeyError) as e: raise TemplateNotFound(template) from e - return loader, name - + return loader, name + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]: - loader, name = self.get_loader(template) - try: - return loader.get_source(environment, name) + loader, name = self.get_loader(template) + try: + return loader.get_source(environment, name) except TemplateNotFound as e: - # re-raise the exception with the correct filename here. - # (the one that includes the prefix) + # re-raise the exception with the correct filename here. + # (the one that includes the prefix) raise TemplateNotFound(template) from e - - @internalcode + + @internalcode def load( self, environment: "Environment", name: str, globals: t.Optional[t.MutableMapping[str, t.Any]] = None, ) -> "Template": - loader, local_name = self.get_loader(name) - try: - return loader.load(environment, local_name, globals) + loader, local_name = self.get_loader(name) + try: + return loader.load(environment, local_name, globals) except TemplateNotFound as e: - # re-raise the exception with the correct filename here. - # (the one that includes the prefix) + # re-raise the exception with the correct filename here. + # (the one that includes the prefix) raise TemplateNotFound(name) from e - + def list_templates(self) -> t.List[str]: - result = [] + result = [] for prefix, loader in self.mapping.items(): - for template in loader.list_templates(): - result.append(prefix + self.delimiter + template) - return result - - -class ChoiceLoader(BaseLoader): - """This loader works like the `PrefixLoader` just that no prefix is - specified. If a template could not be found by one loader the next one - is tried. - - >>> loader = ChoiceLoader([ - ... FileSystemLoader('/path/to/user/templates'), - ... FileSystemLoader('/path/to/system/templates') - ... ]) - - This is useful if you want to allow users to override builtin templates - from a different location. - """ - + for template in loader.list_templates(): + result.append(prefix + self.delimiter + template) + return result + + +class ChoiceLoader(BaseLoader): + """This loader works like the `PrefixLoader` just that no prefix is + specified. If a template could not be found by one loader the next one + is tried. + + >>> loader = ChoiceLoader([ + ... FileSystemLoader('/path/to/user/templates'), + ... FileSystemLoader('/path/to/system/templates') + ... ]) + + This is useful if you want to allow users to override builtin templates + from a different location. + """ + def __init__(self, loaders: t.Sequence[BaseLoader]) -> None: - self.loaders = loaders - + self.loaders = loaders + def get_source( self, environment: "Environment", template: str ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]: - for loader in self.loaders: - try: - return loader.get_source(environment, template) - except TemplateNotFound: - pass - raise TemplateNotFound(template) - - @internalcode + for loader in self.loaders: + try: + return loader.get_source(environment, template) + except TemplateNotFound: + pass + raise TemplateNotFound(template) + + @internalcode def load( self, environment: "Environment", name: str, globals: t.Optional[t.MutableMapping[str, t.Any]] = None, ) -> "Template": - for loader in self.loaders: - try: - return loader.load(environment, name, globals) - except TemplateNotFound: - pass - raise TemplateNotFound(name) - + for loader in self.loaders: + try: + return loader.load(environment, name, globals) + except TemplateNotFound: + pass + raise TemplateNotFound(name) + def list_templates(self) -> t.List[str]: - found = set() - for loader in self.loaders: - found.update(loader.list_templates()) - return sorted(found) - - -class _TemplateModule(ModuleType): - """Like a normal module but with support for weak references""" - - -class ModuleLoader(BaseLoader): - """This loader loads templates from precompiled templates. - - Example usage: - - >>> loader = ChoiceLoader([ - ... ModuleLoader('/path/to/compiled/templates'), - ... FileSystemLoader('/path/to/templates') - ... ]) - - Templates can be precompiled with :meth:`Environment.compile_templates`. - """ - - has_source_access = False - + found = set() + for loader in self.loaders: + found.update(loader.list_templates()) + return sorted(found) + + +class _TemplateModule(ModuleType): + """Like a normal module but with support for weak references""" + + +class ModuleLoader(BaseLoader): + """This loader loads templates from precompiled templates. + + Example usage: + + >>> loader = ChoiceLoader([ + ... ModuleLoader('/path/to/compiled/templates'), + ... FileSystemLoader('/path/to/templates') + ... ]) + + Templates can be precompiled with :meth:`Environment.compile_templates`. + """ + + has_source_access = False + def __init__( self, path: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]] ) -> None: package_name = f"_jinja2_module_templates_{id(self):x}" - - # create a fake module that looks for the templates in the - # path given. - mod = _TemplateModule(package_name) + + # create a fake module that looks for the templates in the + # path given. + mod = _TemplateModule(package_name) if not isinstance(path, abc.Iterable) or isinstance(path, str): - path = [path] - + path = [path] + mod.__path__ = [os.fspath(p) for p in path] # type: ignore - + sys.modules[package_name] = weakref.proxy( mod, lambda x: sys.modules.pop(package_name, None) ) - # the only strong reference, the sys.modules entry is weak - # so that the garbage collector can remove it once the - # loader that created it goes out of business. - self.module = mod - self.package_name = package_name - - @staticmethod + # the only strong reference, the sys.modules entry is weak + # so that the garbage collector can remove it once the + # loader that created it goes out of business. + self.module = mod + self.package_name = package_name + + @staticmethod def get_template_key(name: str) -> str: return "tmpl_" + sha1(name.encode("utf-8")).hexdigest() - - @staticmethod + + @staticmethod def get_module_filename(name: str) -> str: return ModuleLoader.get_template_key(name) + ".py" - - @internalcode + + @internalcode def load( self, environment: "Environment", name: str, globals: t.Optional[t.MutableMapping[str, t.Any]] = None, ) -> "Template": - key = self.get_template_key(name) + key = self.get_template_key(name) module = f"{self.package_name}.{key}" - mod = getattr(self.module, module, None) + mod = getattr(self.module, module, None) - if mod is None: - try: + if mod is None: + try: mod = __import__(module, None, None, ["root"]) except ImportError as e: raise TemplateNotFound(name) from e - - # remove the entry from sys.modules, we only want the attribute - # on the module object we have stored on the loader. - sys.modules.pop(module, None) - + + # remove the entry from sys.modules, we only want the attribute + # on the module object we have stored on the loader. + sys.modules.pop(module, None) + if globals is None: globals = {} - return environment.template_class.from_module_dict( + return environment.template_class.from_module_dict( environment, mod.__dict__, globals ) - - -class ResourceLoader(BaseLoader): - def __init__(self, prefix, module_loader): - self.prefix = prefix - self.module_loader = module_loader - - def get_source(self, environment, template): - if self.module_loader is None: - raise TemplateNotFound(template) - try: + + +class ResourceLoader(BaseLoader): + def __init__(self, prefix, module_loader): + self.prefix = prefix + self.module_loader = module_loader + + def get_source(self, environment, template): + if self.module_loader is None: + raise TemplateNotFound(template) + try: return self.module_loader.get_data(os.path.join(self.prefix, template)).decode('utf-8'), None, None - except IOError: + except IOError: raise TemplateNotFound(template) |