codekingpro/portable-devtools
114k
1"""Classes for managing templates and their runtime and compile time2options.3"""4 5import os6import typing7import typing as t8import weakref9from collections import ChainMap10from functools import lru_cache11from functools import partial12from functools import reduce13from types import CodeType14 15from markupsafe import Markup16 17from . import nodes18from .compiler import CodeGenerator19from .compiler import generate20from .defaults import BLOCK_END_STRING21from .defaults import BLOCK_START_STRING22from .defaults import COMMENT_END_STRING23from .defaults import COMMENT_START_STRING24from .defaults import DEFAULT_FILTERS # type: ignore[attr-defined]25from .defaults import DEFAULT_NAMESPACE26from .defaults import DEFAULT_POLICIES27from .defaults import DEFAULT_TESTS # type: ignore[attr-defined]28from .defaults import KEEP_TRAILING_NEWLINE29from .defaults import LINE_COMMENT_PREFIX30from .defaults import LINE_STATEMENT_PREFIX31from .defaults import LSTRIP_BLOCKS32from .defaults import NEWLINE_SEQUENCE33from .defaults import TRIM_BLOCKS34from .defaults import VARIABLE_END_STRING35from .defaults import VARIABLE_START_STRING36from .exceptions import TemplateNotFound37from .exceptions import TemplateRuntimeError38from .exceptions import TemplatesNotFound39from .exceptions import TemplateSyntaxError40from .exceptions import UndefinedError41from .lexer import get_lexer42from .lexer import Lexer43from .lexer import TokenStream44from .nodes import EvalContext45from .parser import Parser46from .runtime import Context47from .runtime import new_context48from .runtime import Undefined49from .utils import _PassArg50from .utils import concat51from .utils import consume52from .utils import import_string53from .utils import internalcode54from .utils import LRUCache55from .utils import missing56 57if t.TYPE_CHECKING:58 import typing_extensions as te59 60 from .bccache import BytecodeCache61 from .ext import Extension62 from .loaders import BaseLoader63 64_env_bound = t.TypeVar("_env_bound", bound="Environment")65 66 67# for direct template usage we have up to ten living environments68@lru_cache(maxsize=10)69def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:70 """Return a new spontaneous environment. A spontaneous environment71 is used for templates created directly rather than through an72 existing environment.73 74 :param cls: Environment class to create.75 :param args: Positional arguments passed to environment.76 """77 env = cls(*args)78 env.shared = True79 return env80 81 82def create_cache(83 size: int,84) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]:85 """Return the cache class for the given size."""86 if size == 0:87 return None88 89 if size < 0:90 return {}91 92 return LRUCache(size) # type: ignore93 94 95def copy_cache(96 cache: t.Optional[t.MutableMapping[t.Any, t.Any]],97) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]:98 """Create an empty copy of the given cache."""99 if cache is None:100 return None101 102 if type(cache) is dict: # noqa E721103 return {}104 105 return LRUCache(cache.capacity) # type: ignore106 107 108def load_extensions(109 environment: "Environment",110 extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],111) -> t.Dict[str, "Extension"]:112 """Load the extensions from the list and bind it to the environment.113 Returns a dict of instantiated extensions.114 """115 result = {}116 117 for extension in extensions:118 if isinstance(extension, str):119 extension = t.cast(t.Type["Extension"], import_string(extension))120 121 result[extension.identifier] = extension(environment)122 123 return result124 125 126def _environment_config_check(environment: "Environment") -> "Environment":127 """Perform a sanity check on the environment."""128 assert issubclass(129 environment.undefined, Undefined130 ), "'undefined' must be a subclass of 'jinja2.Undefined'."131 assert (132 environment.block_start_string133 != environment.variable_start_string134 != environment.comment_start_string135 ), "block, variable and comment start strings must be different."136 assert environment.newline_sequence in {137 "\r",138 "\r\n",139 "\n",140 }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'."141 return environment142 143 144class Environment:145 r"""The core component of Jinja is the `Environment`. It contains146 important shared variables like configuration, filters, tests,147 globals and others. Instances of this class may be modified if148 they are not shared and if no template was loaded so far.149 Modifications on environments after the first template was loaded150 will lead to surprising effects and undefined behavior.151 152 Here are the possible initialization parameters:153 154 `block_start_string`155 The string marking the beginning of a block. Defaults to ``'{%'``.156 157 `block_end_string`158 The string marking the end of a block. Defaults to ``'%}'``.159 160 `variable_start_string`161 The string marking the beginning of a print statement.162 Defaults to ``'{{'``.163 164 `variable_end_string`165 The string marking the end of a print statement. Defaults to166 ``'}}'``.167 168 `comment_start_string`169 The string marking the beginning of a comment. Defaults to ``'{#'``.170 171 `comment_end_string`172 The string marking the end of a comment. Defaults to ``'#}'``.173 174 `line_statement_prefix`175 If given and a string, this will be used as prefix for line based176 statements. See also :ref:`line-statements`.177 178 `line_comment_prefix`179 If given and a string, this will be used as prefix for line based180 comments. See also :ref:`line-statements`.181 182 .. versionadded:: 2.2183 184 `trim_blocks`185 If this is set to ``True`` the first newline after a block is186 removed (block, not variable tag!). Defaults to `False`.187 188 `lstrip_blocks`189 If this is set to ``True`` leading spaces and tabs are stripped190 from the start of a line to a block. Defaults to `False`.191 192 `newline_sequence`193 The sequence that starts a newline. Must be one of ``'\r'``,194 ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a195 useful default for Linux and OS X systems as well as web196 applications.197 198 `keep_trailing_newline`199 Preserve the trailing newline when rendering templates.200 The default is ``False``, which causes a single newline,201 if present, to be stripped from the end of the template.202 203 .. versionadded:: 2.7204 205 `extensions`206 List of Jinja extensions to use. This can either be import paths207 as strings or extension classes. For more information have a208 look at :ref:`the extensions documentation <jinja-extensions>`.209 210 `optimized`211 should the optimizer be enabled? Default is ``True``.212 213 `undefined`214 :class:`Undefined` or a subclass of it that is used to represent215 undefined values in the template.216 217 `finalize`218 A callable that can be used to process the result of a variable219 expression before it is output. For example one can convert220 ``None`` implicitly into an empty string here.221 222 `autoescape`223 If set to ``True`` the XML/HTML autoescaping feature is enabled by224 default. For more details about autoescaping see225 :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also226 be a callable that is passed the template name and has to227 return ``True`` or ``False`` depending on autoescape should be228 enabled by default.229 230 .. versionchanged:: 2.4231 `autoescape` can now be a function232 233 `loader`234 The template loader for this environment.235 236 `cache_size`237 The size of the cache. Per default this is ``400`` which means238 that if more than 400 templates are loaded the loader will clean239 out the least recently used template. If the cache size is set to240 ``0`` templates are recompiled all the time, if the cache size is241 ``-1`` the cache will not be cleaned.242 243 .. versionchanged:: 2.8244 The cache size was increased to 400 from a low 50.245 246 `auto_reload`247 Some loaders load templates from locations where the template248 sources may change (ie: file system or database). If249 ``auto_reload`` is set to ``True`` (default) every time a template is250 requested the loader checks if the source changed and if yes, it251 will reload the template. For higher performance it's possible to252 disable that.253 254 `bytecode_cache`255 If set to a bytecode cache object, this object will provide a256 cache for the internal Jinja bytecode so that templates don't257 have to be parsed if they were not changed.258 259 See :ref:`bytecode-cache` for more information.260 261 `enable_async`262 If set to true this enables async template execution which263 allows using async functions and generators.264 """265 266 #: if this environment is sandboxed. Modifying this variable won't make267 #: the environment sandboxed though. For a real sandboxed environment268 #: have a look at jinja2.sandbox. This flag alone controls the code269 #: generation by the compiler.270 sandboxed = False271 272 #: True if the environment is just an overlay273 overlayed = False274 275 #: the environment this environment is linked to if it is an overlay276 linked_to: t.Optional["Environment"] = None277 278 #: shared environments have this set to `True`. A shared environment279 #: must not be modified280 shared = False281 282 #: the class that is used for code generation. See283 #: :class:`~jinja2.compiler.CodeGenerator` for more information.284 code_generator_class: t.Type["CodeGenerator"] = CodeGenerator285 286 concat = "".join287 288 #: the context class that is used for templates. See289 #: :class:`~jinja2.runtime.Context` for more information.290 context_class: t.Type[Context] = Context291 292 template_class: t.Type["Template"]293 294 def __init__(295 self,296 block_start_string: str = BLOCK_START_STRING,297 block_end_string: str = BLOCK_END_STRING,298 variable_start_string: str = VARIABLE_START_STRING,299 variable_end_string: str = VARIABLE_END_STRING,300 comment_start_string: str = COMMENT_START_STRING,301 comment_end_string: str = COMMENT_END_STRING,302 line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,303 line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,304 trim_blocks: bool = TRIM_BLOCKS,305 lstrip_blocks: bool = LSTRIP_BLOCKS,306 newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,307 keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,308 extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),309 optimized: bool = True,310 undefined: t.Type[Undefined] = Undefined,311 finalize: t.Optional[t.Callable[..., t.Any]] = None,312 autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,313 loader: t.Optional["BaseLoader"] = None,314 cache_size: int = 400,315 auto_reload: bool = True,316 bytecode_cache: t.Optional["BytecodeCache"] = None,317 enable_async: bool = False,318 ):319 # !!Important notice!!320 # The constructor accepts quite a few arguments that should be321 # passed by keyword rather than position. However it's important to322 # not change the order of arguments because it's used at least323 # internally in those cases:324 # - spontaneous environments (i18n extension and Template)325 # - unittests326 # If parameter changes are required only add parameters at the end327 # and don't change the arguments (or the defaults!) of the arguments328 # existing already.329 330 # lexer / parser information331 self.block_start_string = block_start_string332 self.block_end_string = block_end_string333 self.variable_start_string = variable_start_string334 self.variable_end_string = variable_end_string335 self.comment_start_string = comment_start_string336 self.comment_end_string = comment_end_string337 self.line_statement_prefix = line_statement_prefix338 self.line_comment_prefix = line_comment_prefix339 self.trim_blocks = trim_blocks340 self.lstrip_blocks = lstrip_blocks341 self.newline_sequence = newline_sequence342 self.keep_trailing_newline = keep_trailing_newline343 344 # runtime information345 self.undefined: t.Type[Undefined] = undefined346 self.optimized = optimized347 self.finalize = finalize348 self.autoescape = autoescape349 350 # defaults351 self.filters = DEFAULT_FILTERS.copy()352 self.tests = DEFAULT_TESTS.copy()353 self.globals = DEFAULT_NAMESPACE.copy()354 355 # set the loader provided356 self.loader = loader357 self.cache = create_cache(cache_size)358 self.bytecode_cache = bytecode_cache359 self.auto_reload = auto_reload360 361 # configurable policies362 self.policies = DEFAULT_POLICIES.copy()363 364 # load extensions365 self.extensions = load_extensions(self, extensions)366 367 self.is_async = enable_async368 _environment_config_check(self)369 370 def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:371 """Adds an extension after the environment was created.372 373 .. versionadded:: 2.5374 """375 self.extensions.update(load_extensions(self, [extension]))376 377 def extend(self, **attributes: t.Any) -> None:378 """Add the items to the instance of the environment if they do not exist379 yet. This is used by :ref:`extensions <writing-extensions>` to register380 callbacks and configuration values without breaking inheritance.381 """382 for key, value in attributes.items():383 if not hasattr(self, key):384 setattr(self, key, value)385 386 def overlay(387 self,388 block_start_string: str = missing,389 block_end_string: str = missing,390 variable_start_string: str = missing,391 variable_end_string: str = missing,392 comment_start_string: str = missing,393 comment_end_string: str = missing,394 line_statement_prefix: t.Optional[str] = missing,395 line_comment_prefix: t.Optional[str] = missing,396 trim_blocks: bool = missing,397 lstrip_blocks: bool = missing,398 newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,399 keep_trailing_newline: bool = missing,400 extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,401 optimized: bool = missing,402 undefined: t.Type[Undefined] = missing,403 finalize: t.Optional[t.Callable[..., t.Any]] = missing,404 autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,405 loader: t.Optional["BaseLoader"] = missing,406 cache_size: int = missing,407 auto_reload: bool = missing,408 bytecode_cache: t.Optional["BytecodeCache"] = missing,409 enable_async: bool = False,410 ) -> "Environment":411 """Create a new overlay environment that shares all the data with the412 current environment except for cache and the overridden attributes.413 Extensions cannot be removed for an overlayed environment. An overlayed414 environment automatically gets all the extensions of the environment it415 is linked to plus optional extra extensions.416 417 Creating overlays should happen after the initial environment was set418 up completely. Not all attributes are truly linked, some are just419 copied over so modifications on the original environment may not shine420 through.421 422 .. versionchanged:: 3.1.2423 Added the ``newline_sequence``,, ``keep_trailing_newline``,424 and ``enable_async`` parameters to match ``__init__``.425 """426 args = dict(locals())427 del args["self"], args["cache_size"], args["extensions"], args["enable_async"]428 429 rv = object.__new__(self.__class__)430 rv.__dict__.update(self.__dict__)431 rv.overlayed = True432 rv.linked_to = self433 434 for key, value in args.items():435 if value is not missing:436 setattr(rv, key, value)437 438 if cache_size is not missing:439 rv.cache = create_cache(cache_size)440 else:441 rv.cache = copy_cache(self.cache)442 443 rv.extensions = {}444 for key, value in self.extensions.items():445 rv.extensions[key] = value.bind(rv)446 if extensions is not missing:447 rv.extensions.update(load_extensions(rv, extensions))448 449 if enable_async is not missing:450 rv.is_async = enable_async451 452 return _environment_config_check(rv)453 454 @property455 def lexer(self) -> Lexer:456 """The lexer for this environment."""457 return get_lexer(self)458 459 def iter_extensions(self) -> t.Iterator["Extension"]:460 """Iterates over the extensions by priority."""461 return iter(sorted(self.extensions.values(), key=lambda x: x.priority))462 463 def getitem(464 self, obj: t.Any, argument: t.Union[str, t.Any]465 ) -> t.Union[t.Any, Undefined]:466 """Get an item or attribute of an object but prefer the item."""467 try:468 return obj[argument]469 except (AttributeError, TypeError, LookupError):470 if isinstance(argument, str):471 try:472 attr = str(argument)473 except Exception:474 pass475 else:476 try:477 return getattr(obj, attr)478 except AttributeError:479 pass480 return self.undefined(obj=obj, name=argument)481 482 def getattr(self, obj: t.Any, attribute: str) -> t.Any:483 """Get an item or attribute of an object but prefer the attribute.484 Unlike :meth:`getitem` the attribute *must* be a string.485 """486 try:487 return getattr(obj, attribute)488 except AttributeError:489 pass490 try:491 return obj[attribute]492 except (TypeError, LookupError, AttributeError):493 return self.undefined(obj=obj, name=attribute)494 495 def _filter_test_common(496 self,497 name: t.Union[str, Undefined],498 value: t.Any,499 args: t.Optional[t.Sequence[t.Any]],500 kwargs: t.Optional[t.Mapping[str, t.Any]],501 context: t.Optional[Context],502 eval_ctx: t.Optional[EvalContext],503 is_filter: bool,504 ) -> t.Any:505 if is_filter:506 env_map = self.filters507 type_name = "filter"508 else:509 env_map = self.tests510 type_name = "test"511 512 func = env_map.get(name) # type: ignore513 514 if func is None:515 msg = f"No {type_name} named {name!r}."516 517 if isinstance(name, Undefined):518 try:519 name._fail_with_undefined_error()520 except Exception as e:521 msg = f"{msg} ({e}; did you forget to quote the callable name?)"522 523 raise TemplateRuntimeError(msg)524 525 args = [value, *(args if args is not None else ())]526 kwargs = kwargs if kwargs is not None else {}527 pass_arg = _PassArg.from_obj(func)528 529 if pass_arg is _PassArg.context:530 if context is None:531 raise TemplateRuntimeError(532 f"Attempted to invoke a context {type_name} without context."533 )534 535 args.insert(0, context)536 elif pass_arg is _PassArg.eval_context:537 if eval_ctx is None:538 if context is not None:539 eval_ctx = context.eval_ctx540 else:541 eval_ctx = EvalContext(self)542 543 args.insert(0, eval_ctx)544 elif pass_arg is _PassArg.environment:545 args.insert(0, self)546 547 return func(*args, **kwargs)548 549 def call_filter(550 self,551 name: str,552 value: t.Any,553 args: t.Optional[t.Sequence[t.Any]] = None,554 kwargs: t.Optional[t.Mapping[str, t.Any]] = None,555 context: t.Optional[Context] = None,556 eval_ctx: t.Optional[EvalContext] = None,557 ) -> t.Any:558 """Invoke a filter on a value the same way the compiler does.559 560 This might return a coroutine if the filter is running from an561 environment in async mode and the filter supports async562 execution. It's your responsibility to await this if needed.563 564 .. versionadded:: 2.7565 """566 return self._filter_test_common(567 name, value, args, kwargs, context, eval_ctx, True568 )569 570 def call_test(571 self,572 name: str,573 value: t.Any,574 args: t.Optional[t.Sequence[t.Any]] = None,575 kwargs: t.Optional[t.Mapping[str, t.Any]] = None,576 context: t.Optional[Context] = None,577 eval_ctx: t.Optional[EvalContext] = None,578 ) -> t.Any:579 """Invoke a test on a value the same way the compiler does.580 581 This might return a coroutine if the test is running from an582 environment in async mode and the test supports async execution.583 It's your responsibility to await this if needed.584 585 .. versionchanged:: 3.0586 Tests support ``@pass_context``, etc. decorators. Added587 the ``context`` and ``eval_ctx`` parameters.588 589 .. versionadded:: 2.7590 """591 return self._filter_test_common(592 name, value, args, kwargs, context, eval_ctx, False593 )594 595 @internalcode596 def parse(597 self,598 source: str,599 name: t.Optional[str] = None,600 filename: t.Optional[str] = None,601 ) -> nodes.Template:602 """Parse the sourcecode and return the abstract syntax tree. This603 tree of nodes is used by the compiler to convert the template into604 executable source- or bytecode. This is useful for debugging or to605 extract information from templates.606 607 If you are :ref:`developing Jinja extensions <writing-extensions>`608 this gives you a good overview of the node tree generated.609 """610 try:611 return self._parse(source, name, filename)612 except TemplateSyntaxError:613 self.handle_exception(source=source)614 615 def _parse(616 self, source: str, name: t.Optional[str], filename: t.Optional[str]617 ) -> nodes.Template:618 """Internal parsing function used by `parse` and `compile`."""619 return Parser(self, source, name, filename).parse()620 621 def lex(622 self,623 source: str,624 name: t.Optional[str] = None,625 filename: t.Optional[str] = None,626 ) -> t.Iterator[t.Tuple[int, str, str]]:627 """Lex the given sourcecode and return a generator that yields628 tokens as tuples in the form ``(lineno, token_type, value)``.629 This can be useful for :ref:`extension development <writing-extensions>`630 and debugging templates.631 632 This does not perform preprocessing. If you want the preprocessing633 of the extensions to be applied you have to filter source through634 the :meth:`preprocess` method.635 """636 source = str(source)637 try:638 return self.lexer.tokeniter(source, name, filename)639 except TemplateSyntaxError:640 self.handle_exception(source=source)641 642 def preprocess(643 self,644 source: str,645 name: t.Optional[str] = None,646 filename: t.Optional[str] = None,647 ) -> str:648 """Preprocesses the source with all extensions. This is automatically649 called for all parsing and compiling methods but *not* for :meth:`lex`650 because there you usually only want the actual source tokenized.651 """652 return reduce(653 lambda s, e: e.preprocess(s, name, filename),654 self.iter_extensions(),655 str(source),656 )657 658 def _tokenize(659 self,660 source: str,661 name: t.Optional[str],662 filename: t.Optional[str] = None,663 state: t.Optional[str] = None,664 ) -> TokenStream:665 """Called by the parser to do the preprocessing and filtering666 for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.667 """668 source = self.preprocess(source, name, filename)669 stream = self.lexer.tokenize(source, name, filename, state)670 671 for ext in self.iter_extensions():672 stream = ext.filter_stream(stream) # type: ignore673 674 if not isinstance(stream, TokenStream):675 stream = TokenStream(stream, name, filename)676 677 return stream678 679 def _generate(680 self,681 source: nodes.Template,682 name: t.Optional[str],683 filename: t.Optional[str],684 defer_init: bool = False,685 ) -> str:686 """Internal hook that can be overridden to hook a different generate687 method in.688 689 .. versionadded:: 2.5690 """691 return generate( # type: ignore692 source,693 self,694 name,695 filename,696 defer_init=defer_init,697 optimized=self.optimized,698 )699 700 def _compile(self, source: str, filename: str) -> CodeType:701 """Internal hook that can be overridden to hook a different compile702 method in.703 704 .. versionadded:: 2.5705 """706 return compile(source, filename, "exec")707 708 @typing.overload709 def compile( # type: ignore710 self,711 source: t.Union[str, nodes.Template],712 name: t.Optional[str] = None,713 filename: t.Optional[str] = None,714 raw: "te.Literal[False]" = False,715 defer_init: bool = False,716 ) -> CodeType: ...717 718 @typing.overload719 def compile(720 self,721 source: t.Union[str, nodes.Template],722 name: t.Optional[str] = None,723 filename: t.Optional[str] = None,724 raw: "te.Literal[True]" = ...,725 defer_init: bool = False,726 ) -> str: ...727 728 @internalcode729 def compile(730 self,731 source: t.Union[str, nodes.Template],732 name: t.Optional[str] = None,733 filename: t.Optional[str] = None,734 raw: bool = False,735 defer_init: bool = False,736 ) -> t.Union[str, CodeType]:737 """Compile a node or template source code. The `name` parameter is738 the load name of the template after it was joined using739 :meth:`join_path` if necessary, not the filename on the file system.740 the `filename` parameter is the estimated filename of the template on741 the file system. If the template came from a database or memory this742 can be omitted.743 744 The return value of this method is a python code object. If the `raw`745 parameter is `True` the return value will be a string with python746 code equivalent to the bytecode returned otherwise. This method is747 mainly used internally.748 749 `defer_init` is use internally to aid the module code generator. This750 causes the generated code to be able to import without the global751 environment variable to be set.752 753 .. versionadded:: 2.4754 `defer_init` parameter added.755 """756 source_hint = None757 try:758 if isinstance(source, str):759 source_hint = source760 source = self._parse(source, name, filename)761 source = self._generate(source, name, filename, defer_init=defer_init)762 if raw:763 return source764 if filename is None:765 filename = "<template>"766 return self._compile(source, filename)767 except TemplateSyntaxError:768 self.handle_exception(source=source_hint)769 770 def compile_expression(771 self, source: str, undefined_to_none: bool = True772 ) -> "TemplateExpression":773 """A handy helper method that returns a callable that accepts keyword774 arguments that appear as variables in the expression. If called it775 returns the result of the expression.776 777 This is useful if applications want to use the same rules as Jinja778 in template "configuration files" or similar situations.779 780 Example usage:781 782 >>> env = Environment()783 >>> expr = env.compile_expression('foo == 42')784 >>> expr(foo=23)785 False786 >>> expr(foo=42)787 True788 789 Per default the return value is converted to `None` if the790 expression returns an undefined value. This can be changed791 by setting `undefined_to_none` to `False`.792 793 >>> env.compile_expression('var')() is None794 True795 >>> env.compile_expression('var', undefined_to_none=False)()796 Undefined797 798 .. versionadded:: 2.1799 """800 parser = Parser(self, source, state="variable")801 try:802 expr = parser.parse_expression()803 if not parser.stream.eos:804 raise TemplateSyntaxError(805 "chunk after expression", parser.stream.current.lineno, None, None806 )807 expr.set_environment(self)808 except TemplateSyntaxError:809 self.handle_exception(source=source)810 811 body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]812 template = self.from_string(nodes.Template(body, lineno=1))813 return TemplateExpression(template, undefined_to_none)814 815 def compile_templates(816 self,817 target: t.Union[str, "os.PathLike[str]"],818 extensions: t.Optional[t.Collection[str]] = None,819 filter_func: t.Optional[t.Callable[[str], bool]] = None,820 zip: t.Optional[str] = "deflated",821 log_function: t.Optional[t.Callable[[str], None]] = None,822 ignore_errors: bool = True,823 ) -> None:824 """Finds all the templates the loader can find, compiles them825 and stores them in `target`. If `zip` is `None`, instead of in a826 zipfile, the templates will be stored in a directory.827 By default a deflate zip algorithm is used. To switch to828 the stored algorithm, `zip` can be set to ``'stored'``.829 830 `extensions` and `filter_func` are passed to :meth:`list_templates`.831 Each template returned will be compiled to the target folder or832 zipfile.833 834 By default template compilation errors are ignored. In case a835 log function is provided, errors are logged. If you want template836 syntax errors to abort the compilation you can set `ignore_errors`837 to `False` and you will get an exception on syntax errors.838 839 .. versionadded:: 2.4840 """841 from .loaders import ModuleLoader842 843 if log_function is None:844 845 def log_function(x: str) -> None:846 pass847 848 assert log_function is not None849 assert self.loader is not None, "No loader configured."850 851 def write_file(filename: str, data: str) -> None:852 if zip:853 info = ZipInfo(filename)854 info.external_attr = 0o755 << 16855 zip_file.writestr(info, data)856 else:857 with open(os.path.join(target, filename), "wb") as f:858 f.write(data.encode("utf8"))859 860 if zip is not None:861 from zipfile import ZIP_DEFLATED862 from zipfile import ZIP_STORED863 from zipfile import ZipFile864 from zipfile import ZipInfo865 866 zip_file = ZipFile(867 target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]868 )869 log_function(f"Compiling into Zip archive {target!r}")870 else:871 if not os.path.isdir(target):872 os.makedirs(target)873 log_function(f"Compiling into folder {target!r}")874 875 try:876 for name in self.list_templates(extensions, filter_func):877 source, filename, _ = self.loader.get_source(self, name)878 try:879 code = self.compile(source, name, filename, True, True)880 except TemplateSyntaxError as e:881 if not ignore_errors:882 raise883 log_function(f'Could not compile "{name}": {e}')884 continue885 886 filename = ModuleLoader.get_module_filename(name)887 888 write_file(filename, code)889 log_function(f'Compiled "{name}" as {filename}')890 finally:891 if zip:892 zip_file.close()893 894 log_function("Finished compiling templates")895 896 def list_templates(897 self,898 extensions: t.Optional[t.Collection[str]] = None,899 filter_func: t.Optional[t.Callable[[str], bool]] = None,900 ) -> t.List[str]:901 """Returns a list of templates for this environment. This requires902 that the loader supports the loader's903 :meth:`~BaseLoader.list_templates` method.904 905 If there are other files in the template folder besides the906 actual templates, the returned list can be filtered. There are two907 ways: either `extensions` is set to a list of file extensions for908 templates, or a `filter_func` can be provided which is a callable that909 is passed a template name and should return `True` if it should end up910 in the result list.911 912 If the loader does not support that, a :exc:`TypeError` is raised.913 914 .. versionadded:: 2.4915 """916 assert self.loader is not None, "No loader configured."917 names = self.loader.list_templates()918 919 if extensions is not None:920 if filter_func is not None:921 raise TypeError(922 "either extensions or filter_func can be passed, but not both"923 )924 925 def filter_func(x: str) -> bool:926 return "." in x and x.rsplit(".", 1)[1] in extensions927 928 if filter_func is not None:929 names = [name for name in names if filter_func(name)]930 931 return names932 933 def handle_exception(self, source: t.Optional[str] = None) -> "te.NoReturn":934 """Exception handling helper. This is used internally to either raise935 rewritten exceptions or return a rendered traceback for the template.936 """937 from .debug import rewrite_traceback_stack938 939 raise rewrite_traceback_stack(source=source)940 941 def join_path(self, template: str, parent: str) -> str:942 """Join a template with the parent. By default all the lookups are943 relative to the loader root so this method returns the `template`944 parameter unchanged, but if the paths should be relative to the945 parent template, this function can be used to calculate the real946 template name.947 948 Subclasses may override this method and implement template path949 joining here.950 """951 return template952 953 @internalcode954 def _load_template(955 self, name: str, globals: t.Optional[t.MutableMapping[str, t.Any]]956 ) -> "Template":957 if self.loader is None:958 raise TypeError("no loader for this environment specified")959 cache_key = (weakref.ref(self.loader), name)960 if self.cache is not None:961 template = self.cache.get(cache_key)962 if template is not None and (963 not self.auto_reload or template.is_up_to_date964 ):965 # template.globals is a ChainMap, modifying it will only966 # affect the template, not the environment globals.967 if globals:968 template.globals.update(globals)969 970 return template971 972 template = self.loader.load(self, name, self.make_globals(globals))973 974 if self.cache is not None:975 self.cache[cache_key] = template976 return template977 978 @internalcode979 def get_template(980 self,981 name: t.Union[str, "Template"],982 parent: t.Optional[str] = None,983 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,984 ) -> "Template":985 """Load a template by name with :attr:`loader` and return a986 :class:`Template`. If the template does not exist a987 :exc:`TemplateNotFound` exception is raised.988 989 :param name: Name of the template to load. When loading990 templates from the filesystem, "/" is used as the path991 separator, even on Windows.992 :param parent: The name of the parent template importing this993 template. :meth:`join_path` can be used to implement name994 transformations with this.995 :param globals: Extend the environment :attr:`globals` with996 these extra variables available for all renders of this997 template. If the template has already been loaded and998 cached, its globals are updated with any new items.999 1000 .. versionchanged:: 3.01001 If a template is loaded from cache, ``globals`` will update1002 the template's globals instead of ignoring the new values.1003 1004 .. versionchanged:: 2.41005 If ``name`` is a :class:`Template` object it is returned1006 unchanged.1007 """1008 if isinstance(name, Template):1009 return name1010 if parent is not None:1011 name = self.join_path(name, parent)1012 1013 return self._load_template(name, globals)1014 1015 @internalcode1016 def select_template(1017 self,1018 names: t.Iterable[t.Union[str, "Template"]],1019 parent: t.Optional[str] = None,1020 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,1021 ) -> "Template":1022 """Like :meth:`get_template`, but tries loading multiple names.1023 If none of the names can be loaded a :exc:`TemplatesNotFound`1024 exception is raised.1025 1026 :param names: List of template names to try loading in order.1027 :param parent: The name of the parent template importing this1028 template. :meth:`join_path` can be used to implement name1029 transformations with this.1030 :param globals: Extend the environment :attr:`globals` with1031 these extra variables available for all renders of this1032 template. If the template has already been loaded and1033 cached, its globals are updated with any new items.1034 1035 .. versionchanged:: 3.01036 If a template is loaded from cache, ``globals`` will update1037 the template's globals instead of ignoring the new values.1038 1039 .. versionchanged:: 2.111040 If ``names`` is :class:`Undefined`, an :exc:`UndefinedError`1041 is raised instead. If no templates were found and ``names``1042 contains :class:`Undefined`, the message is more helpful.1043 1044 .. versionchanged:: 2.41045 If ``names`` contains a :class:`Template` object it is1046 returned unchanged.1047 1048 .. versionadded:: 2.31049 """1050 if isinstance(names, Undefined):1051 names._fail_with_undefined_error()1052 1053 if not names:1054 raise TemplatesNotFound(1055 message="Tried to select from an empty list of templates."1056 )1057 1058 for name in names:1059 if isinstance(name, Template):1060 return name1061 if parent is not None:1062 name = self.join_path(name, parent)1063 try:1064 return self._load_template(name, globals)1065 except (TemplateNotFound, UndefinedError):1066 pass1067 raise TemplatesNotFound(names) # type: ignore1068 1069 @internalcode1070 def get_or_select_template(1071 self,1072 template_name_or_list: t.Union[1073 str, "Template", t.List[t.Union[str, "Template"]]1074 ],1075 parent: t.Optional[str] = None,1076 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,1077 ) -> "Template":1078 """Use :meth:`select_template` if an iterable of template names1079 is given, or :meth:`get_template` if one name is given.1080 1081 .. versionadded:: 2.31082 """1083 if isinstance(template_name_or_list, (str, Undefined)):1084 return self.get_template(template_name_or_list, parent, globals)1085 elif isinstance(template_name_or_list, Template):1086 return template_name_or_list1087 return self.select_template(template_name_or_list, parent, globals)1088 1089 def from_string(1090 self,1091 source: t.Union[str, nodes.Template],1092 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,1093 template_class: t.Optional[t.Type["Template"]] = None,1094 ) -> "Template":1095 """Load a template from a source string without using1096 :attr:`loader`.1097 1098 :param source: Jinja source to compile into a template.1099 :param globals: Extend the environment :attr:`globals` with1100 these extra variables available for all renders of this1101 template. If the template has already been loaded and1102 cached, its globals are updated with any new items.1103 :param template_class: Return an instance of this1104 :class:`Template` class.1105 """1106 gs = self.make_globals(globals)1107 cls = template_class or self.template_class1108 return cls.from_code(self, self.compile(source), gs, None)1109 1110 def make_globals(1111 self, d: t.Optional[t.MutableMapping[str, t.Any]]1112 ) -> t.MutableMapping[str, t.Any]:1113 """Make the globals map for a template. Any given template1114 globals overlay the environment :attr:`globals`.1115 1116 Returns a :class:`collections.ChainMap`. This allows any changes1117 to a template's globals to only affect that template, while1118 changes to the environment's globals are still reflected.1119 However, avoid modifying any globals after a template is loaded.1120 1121 :param d: Dict of template-specific globals.1122 1123 .. versionchanged:: 3.01124 Use :class:`collections.ChainMap` to always prevent mutating1125 environment globals.1126 """1127 if d is None:1128 d = {}1129 1130 return ChainMap(d, self.globals)1131 1132 1133class Template:1134 """A compiled template that can be rendered.1135 1136 Use the methods on :class:`Environment` to create or load templates.1137 The environment is used to configure how templates are compiled and1138 behave.1139 1140 It is also possible to create a template object directly. This is1141 not usually recommended. The constructor takes most of the same1142 arguments as :class:`Environment`. All templates created with the1143 same environment arguments share the same ephemeral ``Environment``1144 instance behind the scenes.1145 1146 A template object should be considered immutable. Modifications on1147 the object are not supported.1148 """1149 1150 #: Type of environment to create when creating a template directly1151 #: rather than through an existing environment.1152 environment_class: t.Type[Environment] = Environment1153 1154 environment: Environment1155 globals: t.MutableMapping[str, t.Any]1156 name: t.Optional[str]1157 filename: t.Optional[str]1158 blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]1159 root_render_func: t.Callable[[Context], t.Iterator[str]]1160 _module: t.Optional["TemplateModule"]1161 _debug_info: str1162 _uptodate: t.Optional[t.Callable[[], bool]]1163 1164 def __new__(1165 cls,1166 source: t.Union[str, nodes.Template],1167 block_start_string: str = BLOCK_START_STRING,1168 block_end_string: str = BLOCK_END_STRING,1169 variable_start_string: str = VARIABLE_START_STRING,1170 variable_end_string: str = VARIABLE_END_STRING,1171 comment_start_string: str = COMMENT_START_STRING,1172 comment_end_string: str = COMMENT_END_STRING,1173 line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,1174 line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,1175 trim_blocks: bool = TRIM_BLOCKS,1176 lstrip_blocks: bool = LSTRIP_BLOCKS,1177 newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,1178 keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,1179 extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),1180 optimized: bool = True,1181 undefined: t.Type[Undefined] = Undefined,1182 finalize: t.Optional[t.Callable[..., t.Any]] = None,1183 autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,1184 enable_async: bool = False,1185 ) -> t.Any: # it returns a `Template`, but this breaks the sphinx build...1186 env = get_spontaneous_environment(1187 cls.environment_class, # type: ignore1188 block_start_string,1189 block_end_string,1190 variable_start_string,1191 variable_end_string,1192 comment_start_string,1193 comment_end_string,1194 line_statement_prefix,1195 line_comment_prefix,1196 trim_blocks,1197 lstrip_blocks,1198 newline_sequence,1199 keep_trailing_newline,1200 frozenset(extensions),