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/py2/jinja2/environment.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/py2/jinja2/environment.py')
-rw-r--r-- | contrib/python/Jinja2/py2/jinja2/environment.py | 2050 |
1 files changed, 1025 insertions, 1025 deletions
diff --git a/contrib/python/Jinja2/py2/jinja2/environment.py b/contrib/python/Jinja2/py2/jinja2/environment.py index 0fb5d37933..8430390eea 100644 --- a/contrib/python/Jinja2/py2/jinja2/environment.py +++ b/contrib/python/Jinja2/py2/jinja2/environment.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- """Classes for managing templates and their runtime and compile time options. -""" -import os -import sys -import weakref +""" +import os +import sys +import weakref from functools import partial from functools import reduce - + from markupsafe import Markup - + from . import nodes from ._compat import encode_filename from ._compat import implements_iterator @@ -58,70 +58,70 @@ from .utils import internalcode from .utils import LRUCache from .utils import missing -# for direct template usage we have up to ten living environments -_spontaneous_environments = LRUCache(10) - - +# for direct template usage we have up to ten living environments +_spontaneous_environments = LRUCache(10) + + def get_spontaneous_environment(cls, *args): """Return a new spontaneous environment. A spontaneous environment is used for templates created directly rather than through an existing environment. - + :param cls: Environment class to create. :param args: Positional arguments passed to environment. - """ + """ key = (cls, args) - try: + try: return _spontaneous_environments[key] except KeyError: _spontaneous_environments[key] = env = cls(*args) env.shared = True - return env - - -def create_cache(size): - """Return the cache class for the given size.""" - if size == 0: - return None - if size < 0: - return {} - return LRUCache(size) - - -def copy_cache(cache): - """Create an empty copy of the given cache.""" - if cache is None: - return None - elif type(cache) is dict: - return {} - return LRUCache(cache.capacity) - - -def load_extensions(environment, extensions): - """Load the extensions from the list and bind it to the environment. - Returns a dict of instantiated environments. - """ - result = {} - for extension in extensions: - if isinstance(extension, string_types): - extension = import_string(extension) - result[extension.identifier] = extension(environment) - return result - - -def fail_for_missing_callable(string, name): - msg = string % name - if isinstance(name, Undefined): - try: - name._fail_with_undefined_error() - except Exception as e: + return env + + +def create_cache(size): + """Return the cache class for the given size.""" + if size == 0: + return None + if size < 0: + return {} + return LRUCache(size) + + +def copy_cache(cache): + """Create an empty copy of the given cache.""" + if cache is None: + return None + elif type(cache) is dict: + return {} + return LRUCache(cache.capacity) + + +def load_extensions(environment, extensions): + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated environments. + """ + result = {} + for extension in extensions: + if isinstance(extension, string_types): + extension = import_string(extension) + result[extension.identifier] = extension(environment) + return result + + +def fail_for_missing_callable(string, name): + msg = string % name + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: msg = "%s (%s; did you forget to quote the callable name?)" % (msg, e) - raise TemplateRuntimeError(msg) - - -def _environment_sanity_check(environment): - """Perform a sanity check on the environment.""" + raise TemplateRuntimeError(msg) + + +def _environment_sanity_check(environment): + """Perform a sanity check on the environment.""" assert issubclass( environment.undefined, Undefined ), "undefined must be a subclass of undefined because filters depend on it." @@ -135,156 +135,156 @@ def _environment_sanity_check(environment): "\r\n", "\n", ), "newline_sequence set to unknown line ending string." - return environment - - -class Environment(object): - r"""The core component of Jinja is the `Environment`. It contains - important shared variables like configuration, filters, tests, - globals and others. Instances of this class may be modified if - they are not shared and if no template was loaded so far. - Modifications on environments after the first template was loaded - will lead to surprising effects and undefined behavior. - - Here are the possible initialization parameters: - - `block_start_string` - The string marking the beginning of a block. Defaults to ``'{%'``. - - `block_end_string` - The string marking the end of a block. Defaults to ``'%}'``. - - `variable_start_string` - The string marking the beginning of a print statement. - Defaults to ``'{{'``. - - `variable_end_string` - The string marking the end of a print statement. Defaults to - ``'}}'``. - - `comment_start_string` - The string marking the beginning of a comment. Defaults to ``'{#'``. - - `comment_end_string` - The string marking the end of a comment. Defaults to ``'#}'``. - - `line_statement_prefix` - If given and a string, this will be used as prefix for line based - statements. See also :ref:`line-statements`. - - `line_comment_prefix` - If given and a string, this will be used as prefix for line based - comments. See also :ref:`line-statements`. - - .. versionadded:: 2.2 - - `trim_blocks` - If this is set to ``True`` the first newline after a block is - removed (block, not variable tag!). Defaults to `False`. - - `lstrip_blocks` - If this is set to ``True`` leading spaces and tabs are stripped - from the start of a line to a block. Defaults to `False`. - - `newline_sequence` - The sequence that starts a newline. Must be one of ``'\r'``, - ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a - useful default for Linux and OS X systems as well as web - applications. - - `keep_trailing_newline` - Preserve the trailing newline when rendering templates. - The default is ``False``, which causes a single newline, - if present, to be stripped from the end of the template. - - .. versionadded:: 2.7 - - `extensions` - List of Jinja extensions to use. This can either be import paths - as strings or extension classes. For more information have a - look at :ref:`the extensions documentation <jinja-extensions>`. - - `optimized` - should the optimizer be enabled? Default is ``True``. - - `undefined` - :class:`Undefined` or a subclass of it that is used to represent - undefined values in the template. - - `finalize` - A callable that can be used to process the result of a variable - expression before it is output. For example one can convert - ``None`` implicitly into an empty string here. - - `autoescape` - If set to ``True`` the XML/HTML autoescaping feature is enabled by - default. For more details about autoescaping see + return environment + + +class Environment(object): + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation <jinja-extensions>`. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also - be a callable that is passed the template name and has to - return ``True`` or ``False`` depending on autoescape should be - enabled by default. - - .. versionchanged:: 2.4 - `autoescape` can now be a function - - `loader` - The template loader for this environment. - - `cache_size` - The size of the cache. Per default this is ``400`` which means - that if more than 400 templates are loaded the loader will clean - out the least recently used template. If the cache size is set to - ``0`` templates are recompiled all the time, if the cache size is - ``-1`` the cache will not be cleaned. - - .. versionchanged:: 2.8 - The cache size was increased to 400 from a low 50. - - `auto_reload` - Some loaders load templates from locations where the template - sources may change (ie: file system or database). If - ``auto_reload`` is set to ``True`` (default) every time a template is - requested the loader checks if the source changed and if yes, it - will reload the template. For higher performance it's possible to - disable that. - - `bytecode_cache` - If set to a bytecode cache object, this object will provide a - cache for the internal Jinja bytecode so that templates don't - have to be parsed if they were not changed. - - See :ref:`bytecode-cache` for more information. - - `enable_async` - If set to true this enables async template execution which allows - you to take advantage of newer Python features. This requires - Python 3.6 or later. - """ - - #: if this environment is sandboxed. Modifying this variable won't make - #: the environment sandboxed though. For a real sandboxed environment - #: have a look at jinja2.sandbox. This flag alone controls the code - #: generation by the compiler. - sandboxed = False - - #: True if the environment is just an overlay - overlayed = False - - #: the environment this environment is linked to if it is an overlay - linked_to = None - - #: shared environments have this set to `True`. A shared environment - #: must not be modified - shared = False - - #: the class that is used for code generation. See - #: :class:`~jinja2.compiler.CodeGenerator` for more information. - code_generator_class = CodeGenerator - - #: the context class thatis used for templates. See - #: :class:`~jinja2.runtime.Context` for more information. - context_class = Context - + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which allows + you to take advantage of newer Python features. This requires + Python 3.6 or later. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class = CodeGenerator + + #: the context class thatis used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class = Context + def __init__( self, block_start_string=BLOCK_START_STRING, @@ -310,78 +310,78 @@ class Environment(object): bytecode_cache=None, enable_async=False, ): - # !!Important notice!! - # The constructor accepts quite a few arguments that should be - # passed by keyword rather than position. However it's important to - # not change the order of arguments because it's used at least - # internally in those cases: - # - spontaneous environments (i18n extension and Template) - # - unittests - # If parameter changes are required only add parameters at the end - # and don't change the arguments (or the defaults!) of the arguments - # existing already. - - # lexer / parser information - self.block_start_string = block_start_string - self.block_end_string = block_end_string - self.variable_start_string = variable_start_string - self.variable_end_string = variable_end_string - self.comment_start_string = comment_start_string - self.comment_end_string = comment_end_string - self.line_statement_prefix = line_statement_prefix - self.line_comment_prefix = line_comment_prefix - self.trim_blocks = trim_blocks - self.lstrip_blocks = lstrip_blocks - self.newline_sequence = newline_sequence - self.keep_trailing_newline = keep_trailing_newline - - # runtime information - self.undefined = undefined - self.optimized = optimized - self.finalize = finalize - self.autoescape = autoescape - - # defaults - self.filters = DEFAULT_FILTERS.copy() - self.tests = DEFAULT_TESTS.copy() - self.globals = DEFAULT_NAMESPACE.copy() - - # set the loader provided - self.loader = loader - self.cache = create_cache(cache_size) - self.bytecode_cache = bytecode_cache - self.auto_reload = auto_reload - - # configurable policies - self.policies = DEFAULT_POLICIES.copy() - - # load extensions - self.extensions = load_extensions(self, extensions) - - self.enable_async = enable_async - self.is_async = self.enable_async and have_async_gen + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.enable_async = enable_async + self.is_async = self.enable_async and have_async_gen if self.is_async: # runs patch_all() to enable async support from . import asyncsupport # noqa: F401 - - _environment_sanity_check(self) - - def add_extension(self, extension): - """Adds an extension after the environment was created. - - .. versionadded:: 2.5 - """ - self.extensions.update(load_extensions(self, [extension])) - - def extend(self, **attributes): - """Add the items to the instance of the environment if they do not exist - yet. This is used by :ref:`extensions <writing-extensions>` to register - callbacks and configuration values without breaking inheritance. - """ - for key, value in iteritems(attributes): - if not hasattr(self, key): - setattr(self, key, value) - + + _environment_sanity_check(self) + + def add_extension(self, extension): + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes): + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions <writing-extensions>` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in iteritems(attributes): + if not hasattr(self, key): + setattr(self, key, value) + def overlay( self, block_start_string=missing, @@ -404,185 +404,185 @@ class Environment(object): auto_reload=missing, bytecode_cache=missing, ): - """Create a new overlay environment that shares all the data with the - current environment except for cache and the overridden attributes. - Extensions cannot be removed for an overlayed environment. An overlayed - environment automatically gets all the extensions of the environment it - is linked to plus optional extra extensions. - - Creating overlays should happen after the initial environment was set - up completely. Not all attributes are truly linked, some are just - copied over so modifications on the original environment may not shine - through. - """ - args = dict(locals()) + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + """ + args = dict(locals()) del args["self"], args["cache_size"], args["extensions"] - - rv = object.__new__(self.__class__) - rv.__dict__.update(self.__dict__) - rv.overlayed = True - rv.linked_to = self - - for key, value in iteritems(args): - if value is not missing: - setattr(rv, key, value) - - if cache_size is not missing: - rv.cache = create_cache(cache_size) - else: - rv.cache = copy_cache(self.cache) - - rv.extensions = {} - for key, value in iteritems(self.extensions): - rv.extensions[key] = value.bind(rv) - if extensions is not missing: - rv.extensions.update(load_extensions(rv, extensions)) - - return _environment_sanity_check(rv) - - lexer = property(get_lexer, doc="The lexer for this environment.") - - def iter_extensions(self): - """Iterates over the extensions by priority.""" + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in iteritems(args): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in iteritems(self.extensions): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + return _environment_sanity_check(rv) + + lexer = property(get_lexer, doc="The lexer for this environment.") + + def iter_extensions(self): + """Iterates over the extensions by priority.""" return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) - - def getitem(self, obj, argument): - """Get an item or attribute of an object but prefer the item.""" - try: - return obj[argument] - except (AttributeError, TypeError, LookupError): - if isinstance(argument, string_types): - try: - attr = str(argument) - except Exception: - pass - else: - try: - return getattr(obj, attr) - except AttributeError: - pass - return self.undefined(obj=obj, name=argument) - - def getattr(self, obj, attribute): - """Get an item or attribute of an object but prefer the attribute. - Unlike :meth:`getitem` the attribute *must* be a bytestring. - """ - try: - return getattr(obj, attribute) - except AttributeError: - pass - try: - return obj[attribute] - except (TypeError, LookupError, AttributeError): - return self.undefined(obj=obj, name=attribute) - + + def getitem(self, obj, argument): + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a bytestring. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + def call_filter( self, name, value, args=None, kwargs=None, context=None, eval_ctx=None ): - """Invokes a filter on a value the same way the compiler does it. - - Note that on Python 3 this might return a coroutine in case the - filter is running from an environment in async mode and the filter - supports async execution. It's your responsibility to await this - if needed. - - .. versionadded:: 2.7 - """ - func = self.filters.get(name) - if func is None: + """Invokes a filter on a value the same way the compiler does it. + + Note that on Python 3 this might return a coroutine in case the + filter is running from an environment in async mode and the filter + supports async execution. It's your responsibility to await this + if needed. + + .. versionadded:: 2.7 + """ + func = self.filters.get(name) + if func is None: fail_for_missing_callable("no filter named %r", name) - args = [value] + list(args or ()) + args = [value] + list(args or ()) if getattr(func, "contextfilter", False) is True: - if context is None: + if context is None: raise TemplateRuntimeError( "Attempted to invoke context filter without context" ) - args.insert(0, context) + args.insert(0, context) elif getattr(func, "evalcontextfilter", False) is True: - if eval_ctx is None: - if context is not None: - eval_ctx = context.eval_ctx - else: - eval_ctx = EvalContext(self) - args.insert(0, eval_ctx) + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + args.insert(0, eval_ctx) elif getattr(func, "environmentfilter", False) is True: - args.insert(0, self) - return func(*args, **(kwargs or {})) - - def call_test(self, name, value, args=None, kwargs=None): - """Invokes a test on a value the same way the compiler does it. - - .. versionadded:: 2.7 - """ - func = self.tests.get(name) - if func is None: + args.insert(0, self) + return func(*args, **(kwargs or {})) + + def call_test(self, name, value, args=None, kwargs=None): + """Invokes a test on a value the same way the compiler does it. + + .. versionadded:: 2.7 + """ + func = self.tests.get(name) + if func is None: fail_for_missing_callable("no test named %r", name) - return func(value, *(args or ()), **(kwargs or {})) - - @internalcode - def parse(self, source, name=None, filename=None): - """Parse the sourcecode and return the abstract syntax tree. This - tree of nodes is used by the compiler to convert the template into - executable source- or bytecode. This is useful for debugging or to - extract information from templates. - + return func(value, *(args or ()), **(kwargs or {})) + + @internalcode + def parse(self, source, name=None, filename=None): + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + If you are :ref:`developing Jinja extensions <writing-extensions>` - this gives you a good overview of the node tree generated. - """ - try: - return self._parse(source, name, filename) - except TemplateSyntaxError: + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: self.handle_exception(source=source) - - def _parse(self, source, name, filename): - """Internal parsing function used by `parse` and `compile`.""" - return Parser(self, source, name, encode_filename(filename)).parse() - - def lex(self, source, name=None, filename=None): - """Lex the given sourcecode and return a generator that yields - tokens as tuples in the form ``(lineno, token_type, value)``. - This can be useful for :ref:`extension development <writing-extensions>` - and debugging templates. - - This does not perform preprocessing. If you want the preprocessing - of the extensions to be applied you have to filter source through - the :meth:`preprocess` method. - """ - source = text_type(source) - try: - return self.lexer.tokeniter(source, name, filename) - except TemplateSyntaxError: + + def _parse(self, source, name, filename): + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, encode_filename(filename)).parse() + + def lex(self, source, name=None, filename=None): + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development <writing-extensions>` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = text_type(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: self.handle_exception(source=source) - - def preprocess(self, source, name=None, filename=None): - """Preprocesses the source with all extensions. This is automatically - called for all parsing and compiling methods but *not* for :meth:`lex` - because there you usually only want the actual source tokenized. - """ + + def preprocess(self, source, name=None, filename=None): + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ return reduce( lambda s, e: e.preprocess(s, name, filename), self.iter_extensions(), text_type(source), ) - - def _tokenize(self, source, name, filename=None, state=None): - """Called by the parser to do the preprocessing and filtering - for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. - """ - source = self.preprocess(source, name, filename) - stream = self.lexer.tokenize(source, name, filename, state) - for ext in self.iter_extensions(): - stream = ext.filter_stream(stream) - if not isinstance(stream, TokenStream): - stream = TokenStream(stream, name, filename) - return stream - - def _generate(self, source, name, filename, defer_init=False): - """Internal hook that can be overridden to hook a different generate - method in. - - .. versionadded:: 2.5 - """ + + def _tokenize(self, source, name, filename=None, state=None): + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + return stream + + def _generate(self, source, name, filename, defer_init=False): + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ return generate( source, self, @@ -591,96 +591,96 @@ class Environment(object): defer_init=defer_init, optimized=self.optimized, ) - - def _compile(self, source, filename): - """Internal hook that can be overridden to hook a different compile - method in. - - .. versionadded:: 2.5 - """ + + def _compile(self, source, filename): + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ return compile(source, filename, "exec") - - @internalcode + + @internalcode def compile(self, source, name=None, filename=None, raw=False, defer_init=False): - """Compile a node or template source code. The `name` parameter is - the load name of the template after it was joined using - :meth:`join_path` if necessary, not the filename on the file system. - the `filename` parameter is the estimated filename of the template on - the file system. If the template came from a database or memory this - can be omitted. - - The return value of this method is a python code object. If the `raw` - parameter is `True` the return value will be a string with python - code equivalent to the bytecode returned otherwise. This method is - mainly used internally. - - `defer_init` is use internally to aid the module code generator. This - causes the generated code to be able to import without the global - environment variable to be set. - - .. versionadded:: 2.4 - `defer_init` parameter added. - """ - source_hint = None - try: - if isinstance(source, string_types): - source_hint = source - source = self._parse(source, name, filename) + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, string_types): + source_hint = source + source = self._parse(source, name, filename) source = self._generate(source, name, filename, defer_init=defer_init) - if raw: - return source - if filename is None: + if raw: + return source + if filename is None: filename = "<template>" - else: - filename = encode_filename(filename) - return self._compile(source, filename) - except TemplateSyntaxError: + else: + filename = encode_filename(filename) + return self._compile(source, filename) + except TemplateSyntaxError: self.handle_exception(source=source_hint) - - def compile_expression(self, source, undefined_to_none=True): - """A handy helper method that returns a callable that accepts keyword - arguments that appear as variables in the expression. If called it - returns the result of the expression. - - This is useful if applications want to use the same rules as Jinja - in template "configuration files" or similar situations. - - Example usage: - - >>> env = Environment() - >>> expr = env.compile_expression('foo == 42') - >>> expr(foo=23) - False - >>> expr(foo=42) - True - - Per default the return value is converted to `None` if the - expression returns an undefined value. This can be changed - by setting `undefined_to_none` to `False`. - - >>> env.compile_expression('var')() is None - True - >>> env.compile_expression('var', undefined_to_none=False)() - Undefined - - .. versionadded:: 2.1 - """ + + def compile_expression(self, source, undefined_to_none=True): + """A handy helper method that returns a callable that accepts keyword + arguments that appear as variables in the expression. If called it + returns the result of the expression. + + This is useful if applications want to use the same rules as Jinja + in template "configuration files" or similar situations. + + Example usage: + + >>> env = Environment() + >>> expr = env.compile_expression('foo == 42') + >>> expr(foo=23) + False + >>> expr(foo=42) + True + + Per default the return value is converted to `None` if the + expression returns an undefined value. This can be changed + by setting `undefined_to_none` to `False`. + + >>> env.compile_expression('var')() is None + True + >>> env.compile_expression('var', undefined_to_none=False)() + Undefined + + .. versionadded:: 2.1 + """ parser = Parser(self, source, state="variable") - try: - expr = parser.parse_expression() - if not parser.stream.eos: + try: + expr = parser.parse_expression() + if not parser.stream.eos: raise TemplateSyntaxError( "chunk after expression", parser.stream.current.lineno, None, None ) - expr.set_environment(self) - except TemplateSyntaxError: + expr.set_environment(self) + except TemplateSyntaxError: if sys.exc_info() is not None: self.handle_exception(source=source) body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)] - template = self.from_string(nodes.Template(body, lineno=1)) - return TemplateExpression(template, undefined_to_none) - + template = self.from_string(nodes.Template(body, lineno=1)) + return TemplateExpression(template, undefined_to_none) + def compile_templates( self, target, @@ -691,37 +691,37 @@ class Environment(object): ignore_errors=True, py_compile=False, ): - """Finds all the templates the loader can find, compiles them - and stores them in `target`. If `zip` is `None`, instead of in a - zipfile, the templates will be stored in a directory. - By default a deflate zip algorithm is used. To switch to - the stored algorithm, `zip` can be set to ``'stored'``. - - `extensions` and `filter_func` are passed to :meth:`list_templates`. - Each template returned will be compiled to the target folder or - zipfile. - - By default template compilation errors are ignored. In case a - log function is provided, errors are logged. If you want template - syntax errors to abort the compilation you can set `ignore_errors` - to `False` and you will get an exception on syntax errors. - - If `py_compile` is set to `True` .pyc files will be written to the - target instead of standard .py files. This flag does not do anything - on pypy and Python 3 where pyc files are not picked up by itself and - don't give much benefit. - - .. versionadded:: 2.4 - """ + """Finds all the templates the loader can find, compiles them + and stores them in `target`. If `zip` is `None`, instead of in a + zipfile, the templates will be stored in a directory. + By default a deflate zip algorithm is used. To switch to + the stored algorithm, `zip` can be set to ``'stored'``. + + `extensions` and `filter_func` are passed to :meth:`list_templates`. + Each template returned will be compiled to the target folder or + zipfile. + + By default template compilation errors are ignored. In case a + log function is provided, errors are logged. If you want template + syntax errors to abort the compilation you can set `ignore_errors` + to `False` and you will get an exception on syntax errors. + + If `py_compile` is set to `True` .pyc files will be written to the + target instead of standard .py files. This flag does not do anything + on pypy and Python 3 where pyc files are not picked up by itself and + don't give much benefit. + + .. versionadded:: 2.4 + """ from .loaders import ModuleLoader - - if log_function is None: - + + if log_function is None: + def log_function(x): pass - if py_compile: - if not PY2 or PYPY: + if py_compile: + if not PY2 or PYPY: import warnings warnings.warn( @@ -730,87 +730,87 @@ class Environment(object): DeprecationWarning, stacklevel=2, ) - py_compile = False - else: - import imp - import marshal - + py_compile = False + else: + import imp + import marshal + py_header = imp.get_magic() + u"\xff\xff\xff\xff".encode("iso-8859-15") - # Python 3.3 added a source filesize to the header - if sys.version_info >= (3, 3): + # Python 3.3 added a source filesize to the header + if sys.version_info >= (3, 3): py_header += u"\x00\x00\x00\x00".encode("iso-8859-15") - + def write_file(filename, data): - if zip: - info = ZipInfo(filename) - info.external_attr = 0o755 << 16 - zip_file.writestr(info, data) - else: + if zip: + info = ZipInfo(filename) + info.external_attr = 0o755 << 16 + zip_file.writestr(info, data) + else: if isinstance(data, text_type): data = data.encode("utf8") with open(os.path.join(target, filename), "wb") as f: - f.write(data) - - if zip is not None: - from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED + f.write(data) + + if zip is not None: + from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED zip_file = ZipFile( target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip] ) - log_function('Compiling into Zip archive "%s"' % target) - else: - if not os.path.isdir(target): - os.makedirs(target) - log_function('Compiling into folder "%s"' % target) - - try: - for name in self.list_templates(extensions, filter_func): - source, filename, _ = self.loader.get_source(self, name) - try: - code = self.compile(source, name, filename, True, True) - except TemplateSyntaxError as e: - if not ignore_errors: - raise - log_function('Could not compile "%s": %s' % (name, e)) - continue - - filename = ModuleLoader.get_module_filename(name) - - if py_compile: - c = self._compile(code, encode_filename(filename)) + log_function('Compiling into Zip archive "%s"' % target) + else: + if not os.path.isdir(target): + os.makedirs(target) + log_function('Compiling into folder "%s"' % target) + + try: + for name in self.list_templates(extensions, filter_func): + source, filename, _ = self.loader.get_source(self, name) + try: + code = self.compile(source, name, filename, True, True) + except TemplateSyntaxError as e: + if not ignore_errors: + raise + log_function('Could not compile "%s": %s' % (name, e)) + continue + + filename = ModuleLoader.get_module_filename(name) + + if py_compile: + c = self._compile(code, encode_filename(filename)) write_file(filename + "c", py_header + marshal.dumps(c)) log_function('Byte-compiled "%s" as %s' % (name, filename + "c")) - else: + else: write_file(filename, code) - log_function('Compiled "%s" as %s' % (name, filename)) - finally: - if zip: - zip_file.close() - + log_function('Compiled "%s" as %s' % (name, filename)) + finally: + if zip: + zip_file.close() + log_function("Finished compiling templates") - - def list_templates(self, extensions=None, filter_func=None): - """Returns a list of templates for this environment. This requires - that the loader supports the loader's - :meth:`~BaseLoader.list_templates` method. - - If there are other files in the template folder besides the - actual templates, the returned list can be filtered. There are two - ways: either `extensions` is set to a list of file extensions for - templates, or a `filter_func` can be provided which is a callable that - is passed a template name and should return `True` if it should end up - in the result list. - - If the loader does not support that, a :exc:`TypeError` is raised. - - .. versionadded:: 2.4 - """ + + def list_templates(self, extensions=None, filter_func=None): + """Returns a list of templates for this environment. This requires + that the loader supports the loader's + :meth:`~BaseLoader.list_templates` method. + + If there are other files in the template folder besides the + actual templates, the returned list can be filtered. There are two + ways: either `extensions` is set to a list of file extensions for + templates, or a `filter_func` can be provided which is a callable that + is passed a template name and should return `True` if it should end up + in the result list. + + If the loader does not support that, a :exc:`TypeError` is raised. + + .. versionadded:: 2.4 + """ names = self.loader.list_templates() - if extensions is not None: - if filter_func is not None: + if extensions is not None: + if filter_func is not None: raise TypeError( "either extensions or filter_func can be passed, but not both" ) @@ -818,165 +818,165 @@ class Environment(object): def filter_func(x): return "." in x and x.rsplit(".", 1)[1] in extensions - if filter_func is not None: + if filter_func is not None: names = [name for name in names if filter_func(name)] - + return names def handle_exception(self, source=None): - """Exception handling helper. This is used internally to either raise - rewritten exceptions or return a rendered traceback for the template. - """ + """Exception handling helper. This is used internally to either raise + rewritten exceptions or return a rendered traceback for the template. + """ from .debug import rewrite_traceback_stack - + reraise(*rewrite_traceback_stack(source=source)) - - def join_path(self, template, parent): - """Join a template with the parent. By default all the lookups are - relative to the loader root so this method returns the `template` - parameter unchanged, but if the paths should be relative to the - parent template, this function can be used to calculate the real - template name. - - Subclasses may override this method and implement template path - joining here. - """ - return template - - @internalcode - def _load_template(self, name, globals): - if self.loader is None: + + def join_path(self, template, parent): + """Join a template with the parent. By default all the lookups are + relative to the loader root so this method returns the `template` + parameter unchanged, but if the paths should be relative to the + parent template, this function can be used to calculate the real + template name. + + Subclasses may override this method and implement template path + joining here. + """ + return template + + @internalcode + def _load_template(self, name, globals): + if self.loader is None: raise TypeError("no loader for this environment specified") - cache_key = (weakref.ref(self.loader), name) - if self.cache is not None: - template = self.cache.get(cache_key) + cache_key = (weakref.ref(self.loader), name) + if self.cache is not None: + template = self.cache.get(cache_key) if template is not None and ( not self.auto_reload or template.is_up_to_date ): - return template - template = self.loader.load(self, name, globals) - if self.cache is not None: - self.cache[cache_key] = template - return template - - @internalcode - def get_template(self, name, parent=None, globals=None): - """Load a template from the loader. If a loader is configured this - method asks the loader for the template and returns a :class:`Template`. - If the `parent` parameter is not `None`, :meth:`join_path` is called - to get the real template name before loading. - - The `globals` parameter can be used to provide template wide globals. - These variables are available in the context at render time. - - If the template does not exist a :exc:`TemplateNotFound` exception is - raised. - - .. versionchanged:: 2.4 - If `name` is a :class:`Template` object it is returned from the - function unchanged. - """ - if isinstance(name, Template): - return name - if parent is not None: - name = self.join_path(name, parent) - return self._load_template(name, self.make_globals(globals)) - - @internalcode - def select_template(self, names, parent=None, globals=None): - """Works like :meth:`get_template` but tries a number of templates - before it fails. If it cannot find any of the templates, it will - raise a :exc:`TemplatesNotFound` exception. - + return template + template = self.loader.load(self, name, globals) + if self.cache is not None: + self.cache[cache_key] = template + return template + + @internalcode + def get_template(self, name, parent=None, globals=None): + """Load a template from the loader. If a loader is configured this + method asks the loader for the template and returns a :class:`Template`. + If the `parent` parameter is not `None`, :meth:`join_path` is called + to get the real template name before loading. + + The `globals` parameter can be used to provide template wide globals. + These variables are available in the context at render time. + + If the template does not exist a :exc:`TemplateNotFound` exception is + raised. + + .. versionchanged:: 2.4 + If `name` is a :class:`Template` object it is returned from the + function unchanged. + """ + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + return self._load_template(name, self.make_globals(globals)) + + @internalcode + def select_template(self, names, parent=None, globals=None): + """Works like :meth:`get_template` but tries a number of templates + before it fails. If it cannot find any of the templates, it will + raise a :exc:`TemplatesNotFound` exception. + .. versionchanged:: 2.11 If names is :class:`Undefined`, an :exc:`UndefinedError` is raised instead. If no templates were found and names contains :class:`Undefined`, the message is more helpful. - - .. versionchanged:: 2.4 - If `names` contains a :class:`Template` object it is returned - from the function unchanged. + + .. versionchanged:: 2.4 + If `names` contains a :class:`Template` object it is returned + from the function unchanged. .. versionadded:: 2.3 - """ + """ if isinstance(names, Undefined): names._fail_with_undefined_error() - if not names: + if not names: raise TemplatesNotFound( message=u"Tried to select from an empty list " u"of templates." ) - globals = self.make_globals(globals) - for name in names: - if isinstance(name, Template): - return name - if parent is not None: - name = self.join_path(name, parent) - try: - return self._load_template(name, globals) + globals = self.make_globals(globals) + for name in names: + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + try: + return self._load_template(name, globals) except (TemplateNotFound, UndefinedError): - pass - raise TemplatesNotFound(names) - - @internalcode + pass + raise TemplatesNotFound(names) + + @internalcode def get_or_select_template(self, template_name_or_list, parent=None, globals=None): - """Does a typecheck and dispatches to :meth:`select_template` - if an iterable of template names is given, otherwise to - :meth:`get_template`. - - .. versionadded:: 2.3 - """ + """Does a typecheck and dispatches to :meth:`select_template` + if an iterable of template names is given, otherwise to + :meth:`get_template`. + + .. versionadded:: 2.3 + """ if isinstance(template_name_or_list, (string_types, Undefined)): - return self.get_template(template_name_or_list, parent, globals) - elif isinstance(template_name_or_list, Template): - return template_name_or_list - return self.select_template(template_name_or_list, parent, globals) - - def from_string(self, source, globals=None, template_class=None): - """Load a template from a string. This parses the source given and - returns a :class:`Template` object. - """ - globals = self.make_globals(globals) - cls = template_class or self.template_class - return cls.from_code(self, self.compile(source), globals, None) - - def make_globals(self, d): - """Return a dict for the globals.""" - if not d: - return self.globals - return dict(self.globals, **d) - - -class Template(object): - """The central template object. This class represents a compiled template - and is used to evaluate it. - - Normally the template object is generated from an :class:`Environment` but - it also has a constructor that makes it possible to create a template - instance directly using the constructor. It takes the same arguments as - the environment constructor but it's not possible to specify a loader. - - Every template object has a few methods and members that are guaranteed - to exist. However it's important that a template object should be - considered immutable. Modifications on the object are not supported. - - Template objects created from the constructor rather than an environment - do have an `environment` attribute that points to a temporary environment - that is probably shared with other templates created with the constructor - and compatible settings. - - >>> template = Template('Hello {{ name }}!') - >>> template.render(name='John Doe') == u'Hello John Doe!' - True - >>> stream = template.stream(name='John Doe') - >>> next(stream) == u'Hello John Doe!' - True - >>> next(stream) - Traceback (most recent call last): - ... - StopIteration - """ - + return self.get_template(template_name_or_list, parent, globals) + elif isinstance(template_name_or_list, Template): + return template_name_or_list + return self.select_template(template_name_or_list, parent, globals) + + def from_string(self, source, globals=None, template_class=None): + """Load a template from a string. This parses the source given and + returns a :class:`Template` object. + """ + globals = self.make_globals(globals) + cls = template_class or self.template_class + return cls.from_code(self, self.compile(source), globals, None) + + def make_globals(self, d): + """Return a dict for the globals.""" + if not d: + return self.globals + return dict(self.globals, **d) + + +class Template(object): + """The central template object. This class represents a compiled template + and is used to evaluate it. + + Normally the template object is generated from an :class:`Environment` but + it also has a constructor that makes it possible to create a template + instance directly using the constructor. It takes the same arguments as + the environment constructor but it's not possible to specify a loader. + + Every template object has a few methods and members that are guaranteed + to exist. However it's important that a template object should be + considered immutable. Modifications on the object are not supported. + + Template objects created from the constructor rather than an environment + do have an `environment` attribute that points to a temporary environment + that is probably shared with other templates created with the constructor + and compatible settings. + + >>> template = Template('Hello {{ name }}!') + >>> template.render(name='John Doe') == u'Hello John Doe!' + True + >>> stream = template.stream(name='John Doe') + >>> next(stream) == u'Hello John Doe!' + True + >>> next(stream) + Traceback (most recent call last): + ... + StopIteration + """ + #: Type of environment to create when creating a template directly #: rather than through an existing environment. environment_class = Environment @@ -1003,7 +1003,7 @@ class Template(object): autoescape=False, enable_async=False, ): - env = get_spontaneous_environment( + env = get_spontaneous_environment( cls.environment_class, block_start_string, block_end_string, @@ -1028,335 +1028,335 @@ class Template(object): None, enable_async, ) - return env.from_string(source, template_class=cls) - - @classmethod - def from_code(cls, environment, code, globals, uptodate=None): - """Creates a template object from compiled code and the globals. This - is used by the loaders and environment to create a template object. - """ + return env.from_string(source, template_class=cls) + + @classmethod + def from_code(cls, environment, code, globals, uptodate=None): + """Creates a template object from compiled code and the globals. This + is used by the loaders and environment to create a template object. + """ namespace = {"environment": environment, "__file__": code.co_filename} - exec(code, namespace) - rv = cls._from_namespace(environment, namespace, globals) - rv._uptodate = uptodate - return rv - - @classmethod - def from_module_dict(cls, environment, module_dict, globals): - """Creates a template object from a module. This is used by the - module loader to create a template object. - - .. versionadded:: 2.4 - """ - return cls._from_namespace(environment, module_dict, globals) - - @classmethod - def _from_namespace(cls, environment, namespace, globals): - t = object.__new__(cls) - t.environment = environment - t.globals = globals + exec(code, namespace) + rv = cls._from_namespace(environment, namespace, globals) + rv._uptodate = uptodate + return rv + + @classmethod + def from_module_dict(cls, environment, module_dict, globals): + """Creates a template object from a module. This is used by the + module loader to create a template object. + + .. versionadded:: 2.4 + """ + return cls._from_namespace(environment, module_dict, globals) + + @classmethod + def _from_namespace(cls, environment, namespace, globals): + t = object.__new__(cls) + t.environment = environment + t.globals = globals t.name = namespace["name"] t.filename = namespace["__file__"] t.blocks = namespace["blocks"] - - # render function and module + + # render function and module t.root_render_func = namespace["root"] - t._module = None - - # debug and loader helpers + t._module = None + + # debug and loader helpers t._debug_info = namespace["debug_info"] - t._uptodate = None - - # store the reference + t._uptodate = None + + # store the reference namespace["environment"] = environment namespace["__jinja_template__"] = t - - return t - - def render(self, *args, **kwargs): - """This method accepts the same arguments as the `dict` constructor: - A dict, a dict subclass or some keyword arguments. If no arguments - are given the context will be empty. These two calls do the same:: - - template.render(knights='that say nih') - template.render({'knights': 'that say nih'}) - - This will return the rendered template as unicode string. - """ - vars = dict(*args, **kwargs) - try: - return concat(self.root_render_func(self.new_context(vars))) - except Exception: + + return t + + def render(self, *args, **kwargs): + """This method accepts the same arguments as the `dict` constructor: + A dict, a dict subclass or some keyword arguments. If no arguments + are given the context will be empty. These two calls do the same:: + + template.render(knights='that say nih') + template.render({'knights': 'that say nih'}) + + This will return the rendered template as unicode string. + """ + vars = dict(*args, **kwargs) + try: + return concat(self.root_render_func(self.new_context(vars))) + except Exception: self.environment.handle_exception() - - def render_async(self, *args, **kwargs): - """This works similar to :meth:`render` but returns a coroutine - that when awaited returns the entire rendered template string. This - requires the async feature to be enabled. - - Example usage:: - - await template.render_async(knights='that say nih; asynchronously') - """ - # see asyncsupport for the actual implementation + + def render_async(self, *args, **kwargs): + """This works similar to :meth:`render` but returns a coroutine + that when awaited returns the entire rendered template string. This + requires the async feature to be enabled. + + Example usage:: + + await template.render_async(knights='that say nih; asynchronously') + """ + # see asyncsupport for the actual implementation raise NotImplementedError( "This feature is not available for this version of Python" ) - - def stream(self, *args, **kwargs): - """Works exactly like :meth:`generate` but returns a - :class:`TemplateStream`. - """ - return TemplateStream(self.generate(*args, **kwargs)) - - def generate(self, *args, **kwargs): - """For very large templates it can be useful to not render the whole - template at once but evaluate each statement after another and yield - piece for piece. This method basically does exactly that and returns - a generator that yields one item after another as unicode strings. - - It accepts the same arguments as :meth:`render`. - """ - vars = dict(*args, **kwargs) - try: - for event in self.root_render_func(self.new_context(vars)): - yield event - except Exception: + + def stream(self, *args, **kwargs): + """Works exactly like :meth:`generate` but returns a + :class:`TemplateStream`. + """ + return TemplateStream(self.generate(*args, **kwargs)) + + def generate(self, *args, **kwargs): + """For very large templates it can be useful to not render the whole + template at once but evaluate each statement after another and yield + piece for piece. This method basically does exactly that and returns + a generator that yields one item after another as unicode strings. + + It accepts the same arguments as :meth:`render`. + """ + vars = dict(*args, **kwargs) + try: + for event in self.root_render_func(self.new_context(vars)): + yield event + except Exception: yield self.environment.handle_exception() - - def generate_async(self, *args, **kwargs): - """An async version of :meth:`generate`. Works very similarly but - returns an async iterator instead. - """ - # see asyncsupport for the actual implementation + + def generate_async(self, *args, **kwargs): + """An async version of :meth:`generate`. Works very similarly but + returns an async iterator instead. + """ + # see asyncsupport for the actual implementation raise NotImplementedError( "This feature is not available for this version of Python" ) - - def new_context(self, vars=None, shared=False, locals=None): - """Create a new :class:`Context` for this template. The vars - provided will be passed to the template. Per default the globals - are added to the context. If shared is set to `True` the data + + def new_context(self, vars=None, shared=False, locals=None): + """Create a new :class:`Context` for this template. The vars + provided will be passed to the template. Per default the globals + are added to the context. If shared is set to `True` the data is passed as is to the context without adding the globals. - - `locals` can be a dict of local variables for internal usage. - """ + + `locals` can be a dict of local variables for internal usage. + """ return new_context( self.environment, self.name, self.blocks, vars, shared, self.globals, locals ) - - def make_module(self, vars=None, shared=False, locals=None): - """This method works like the :attr:`module` attribute when called - without arguments but it will evaluate the template on every call - rather than caching it. It's also possible to provide - a dict which is then used as context. The arguments are the same - as for the :meth:`new_context` method. - """ - return TemplateModule(self, self.new_context(vars, shared, locals)) - - def make_module_async(self, vars=None, shared=False, locals=None): - """As template module creation can invoke template code for + + def make_module(self, vars=None, shared=False, locals=None): + """This method works like the :attr:`module` attribute when called + without arguments but it will evaluate the template on every call + rather than caching it. It's also possible to provide + a dict which is then used as context. The arguments are the same + as for the :meth:`new_context` method. + """ + return TemplateModule(self, self.new_context(vars, shared, locals)) + + def make_module_async(self, vars=None, shared=False, locals=None): + """As template module creation can invoke template code for asynchronous executions this method must be used instead of the - normal :meth:`make_module` one. Likewise the module attribute - becomes unavailable in async mode. - """ - # see asyncsupport for the actual implementation + normal :meth:`make_module` one. Likewise the module attribute + becomes unavailable in async mode. + """ + # see asyncsupport for the actual implementation raise NotImplementedError( "This feature is not available for this version of Python" ) - - @internalcode - def _get_default_module(self): - if self._module is not None: - return self._module - self._module = rv = self.make_module() - return rv - - @property - def module(self): - """The template as module. This is used for imports in the - template runtime but is also useful if one wants to access - exported template variables from the Python layer: - - >>> t = Template('{% macro foo() %}42{% endmacro %}23') - >>> str(t.module) - '23' - >>> t.module.foo() == u'42' - True - - This attribute is not available if async mode is enabled. - """ - return self._get_default_module() - - def get_corresponding_lineno(self, lineno): - """Return the source line number of a line number in the - generated bytecode as they are not in sync. - """ - for template_line, code_line in reversed(self.debug_info): - if code_line <= lineno: - return template_line - return 1 - - @property - def is_up_to_date(self): - """If this variable is `False` there is a newer version available.""" - if self._uptodate is None: - return True - return self._uptodate() - - @property - def debug_info(self): - """The debug info mapping.""" + + @internalcode + def _get_default_module(self): + if self._module is not None: + return self._module + self._module = rv = self.make_module() + return rv + + @property + def module(self): + """The template as module. This is used for imports in the + template runtime but is also useful if one wants to access + exported template variables from the Python layer: + + >>> t = Template('{% macro foo() %}42{% endmacro %}23') + >>> str(t.module) + '23' + >>> t.module.foo() == u'42' + True + + This attribute is not available if async mode is enabled. + """ + return self._get_default_module() + + def get_corresponding_lineno(self, lineno): + """Return the source line number of a line number in the + generated bytecode as they are not in sync. + """ + for template_line, code_line in reversed(self.debug_info): + if code_line <= lineno: + return template_line + return 1 + + @property + def is_up_to_date(self): + """If this variable is `False` there is a newer version available.""" + if self._uptodate is None: + return True + return self._uptodate() + + @property + def debug_info(self): + """The debug info mapping.""" if self._debug_info: return [tuple(map(int, x.split("="))) for x in self._debug_info.split("&")] return [] - - def __repr__(self): - if self.name is None: + + def __repr__(self): + if self.name is None: name = "memory:%x" % id(self) - else: - name = repr(self.name) + else: + name = repr(self.name) return "<%s %s>" % (self.__class__.__name__, name) - - -@implements_to_string -class TemplateModule(object): - """Represents an imported template. All the exported names of the - template are available as attributes on this object. Additionally - converting it into an unicode- or bytestrings renders the contents. - """ - - def __init__(self, template, context, body_stream=None): - if body_stream is None: - if context.environment.is_async: + + +@implements_to_string +class TemplateModule(object): + """Represents an imported template. All the exported names of the + template are available as attributes on this object. Additionally + converting it into an unicode- or bytestrings renders the contents. + """ + + def __init__(self, template, context, body_stream=None): + if body_stream is None: + if context.environment.is_async: raise RuntimeError( "Async mode requires a body stream " "to be passed to a template module. Use " "the async methods of the API you are " "using." ) - body_stream = list(template.root_render_func(context)) - self._body_stream = body_stream - self.__dict__.update(context.get_exported()) - self.__name__ = template.name - - def __html__(self): - return Markup(concat(self._body_stream)) - - def __str__(self): - return concat(self._body_stream) - - def __repr__(self): - if self.__name__ is None: + body_stream = list(template.root_render_func(context)) + self._body_stream = body_stream + self.__dict__.update(context.get_exported()) + self.__name__ = template.name + + def __html__(self): + return Markup(concat(self._body_stream)) + + def __str__(self): + return concat(self._body_stream) + + def __repr__(self): + if self.__name__ is None: name = "memory:%x" % id(self) - else: - name = repr(self.__name__) + else: + name = repr(self.__name__) return "<%s %s>" % (self.__class__.__name__, name) - - -class TemplateExpression(object): - """The :meth:`jinja2.Environment.compile_expression` method returns an - instance of this object. It encapsulates the expression-like access - to the template with an expression it wraps. - """ - - def __init__(self, template, undefined_to_none): - self._template = template - self._undefined_to_none = undefined_to_none - - def __call__(self, *args, **kwargs): - context = self._template.new_context(dict(*args, **kwargs)) - consume(self._template.root_render_func(context)) + + +class TemplateExpression(object): + """The :meth:`jinja2.Environment.compile_expression` method returns an + instance of this object. It encapsulates the expression-like access + to the template with an expression it wraps. + """ + + def __init__(self, template, undefined_to_none): + self._template = template + self._undefined_to_none = undefined_to_none + + def __call__(self, *args, **kwargs): + context = self._template.new_context(dict(*args, **kwargs)) + consume(self._template.root_render_func(context)) rv = context.vars["result"] - if self._undefined_to_none and isinstance(rv, Undefined): - rv = None - return rv - - -@implements_iterator -class TemplateStream(object): - """A template stream works pretty much like an ordinary python generator - but it can buffer multiple items to reduce the number of total iterations. - Per default the output is unbuffered which means that for every unbuffered - instruction in the template one unicode string is yielded. - - If buffering is enabled with a buffer size of 5, five items are combined - into a new unicode string. This is mainly useful if you are streaming - big templates to a client via WSGI which flushes after each iteration. - """ - - def __init__(self, gen): - self._gen = gen - self.disable_buffering() - + if self._undefined_to_none and isinstance(rv, Undefined): + rv = None + return rv + + +@implements_iterator +class TemplateStream(object): + """A template stream works pretty much like an ordinary python generator + but it can buffer multiple items to reduce the number of total iterations. + Per default the output is unbuffered which means that for every unbuffered + instruction in the template one unicode string is yielded. + + If buffering is enabled with a buffer size of 5, five items are combined + into a new unicode string. This is mainly useful if you are streaming + big templates to a client via WSGI which flushes after each iteration. + """ + + def __init__(self, gen): + self._gen = gen + self.disable_buffering() + def dump(self, fp, encoding=None, errors="strict"): - """Dump the complete stream into a file or file-like object. - Per default unicode strings are written, if you want to encode - before writing specify an `encoding`. - - Example usage:: - - Template('Hello {{ name }}!').stream(name='foo').dump('hello.html') - """ - close = False - if isinstance(fp, string_types): - if encoding is None: + """Dump the complete stream into a file or file-like object. + Per default unicode strings are written, if you want to encode + before writing specify an `encoding`. + + Example usage:: + + Template('Hello {{ name }}!').stream(name='foo').dump('hello.html') + """ + close = False + if isinstance(fp, string_types): + if encoding is None: encoding = "utf-8" fp = open(fp, "wb") - close = True - try: - if encoding is not None: - iterable = (x.encode(encoding, errors) for x in self) - else: - iterable = self + close = True + try: + if encoding is not None: + iterable = (x.encode(encoding, errors) for x in self) + else: + iterable = self if hasattr(fp, "writelines"): - fp.writelines(iterable) - else: - for item in iterable: - fp.write(item) - finally: - if close: - fp.close() - - def disable_buffering(self): - """Disable the output buffering.""" - self._next = partial(next, self._gen) - self.buffered = False - - def _buffered_generator(self, size): - buf = [] - c_size = 0 - push = buf.append - - while 1: - try: - while c_size < size: - c = next(self._gen) - push(c) - if c: - c_size += 1 - except StopIteration: - if not c_size: - return - yield concat(buf) - del buf[:] - c_size = 0 - - def enable_buffering(self, size=5): - """Enable buffering. Buffer `size` items before yielding them.""" - if size <= 1: + fp.writelines(iterable) + else: + for item in iterable: + fp.write(item) + finally: + if close: + fp.close() + + def disable_buffering(self): + """Disable the output buffering.""" + self._next = partial(next, self._gen) + self.buffered = False + + def _buffered_generator(self, size): + buf = [] + c_size = 0 + push = buf.append + + while 1: + try: + while c_size < size: + c = next(self._gen) + push(c) + if c: + c_size += 1 + except StopIteration: + if not c_size: + return + yield concat(buf) + del buf[:] + c_size = 0 + + def enable_buffering(self, size=5): + """Enable buffering. Buffer `size` items before yielding them.""" + if size <= 1: raise ValueError("buffer size too small") - - self.buffered = True - self._next = partial(next, self._buffered_generator(size)) - - def __iter__(self): - return self - - def __next__(self): - return self._next() - - -# hook in default template class. if anyone reads this comment: ignore that -# it's possible to use custom templates ;-) -Environment.template_class = Template + + self.buffered = True + self._next = partial(next, self._buffered_generator(size)) + + def __iter__(self): + return self + + def __next__(self): + return self._next() + + +# hook in default template class. if anyone reads this comment: ignore that +# it's possible to use custom templates ;-) +Environment.template_class = Template |