Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jinja2/runtime.py: 49%
484 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
1"""The runtime functions and state used by compiled templates."""
2import functools
3import sys
4import typing as t
5from collections import abc
6from itertools import chain
8from markupsafe import escape # noqa: F401
9from markupsafe import Markup
10from markupsafe import soft_str
12from .async_utils import auto_aiter
13from .async_utils import auto_await # noqa: F401
14from .exceptions import TemplateNotFound # noqa: F401
15from .exceptions import TemplateRuntimeError # noqa: F401
16from .exceptions import UndefinedError
17from .nodes import EvalContext
18from .utils import _PassArg
19from .utils import concat
20from .utils import internalcode
21from .utils import missing
22from .utils import Namespace # noqa: F401
23from .utils import object_type_repr
24from .utils import pass_eval_context
26V = t.TypeVar("V")
27F = t.TypeVar("F", bound=t.Callable[..., t.Any])
29if t.TYPE_CHECKING:
30 import logging
31 import typing_extensions as te
32 from .environment import Environment
34 class LoopRenderFunc(te.Protocol):
35 def __call__(
36 self,
37 reciter: t.Iterable[V],
38 loop_render_func: "LoopRenderFunc",
39 depth: int = 0,
40 ) -> str:
41 ...
44# these variables are exported to the template runtime
45exported = [
46 "LoopContext",
47 "TemplateReference",
48 "Macro",
49 "Markup",
50 "TemplateRuntimeError",
51 "missing",
52 "escape",
53 "markup_join",
54 "str_join",
55 "identity",
56 "TemplateNotFound",
57 "Namespace",
58 "Undefined",
59 "internalcode",
60]
61async_exported = [
62 "AsyncLoopContext",
63 "auto_aiter",
64 "auto_await",
65]
68def identity(x: V) -> V:
69 """Returns its argument. Useful for certain things in the
70 environment.
71 """
72 return x
75def markup_join(seq: t.Iterable[t.Any]) -> str:
76 """Concatenation that escapes if necessary and converts to string."""
77 buf = []
78 iterator = map(soft_str, seq)
79 for arg in iterator:
80 buf.append(arg)
81 if hasattr(arg, "__html__"):
82 return Markup("").join(chain(buf, iterator))
83 return concat(buf)
86def str_join(seq: t.Iterable[t.Any]) -> str:
87 """Simple args to string conversion and concatenation."""
88 return concat(map(str, seq))
91def new_context(
92 environment: "Environment",
93 template_name: t.Optional[str],
94 blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
95 vars: t.Optional[t.Dict[str, t.Any]] = None,
96 shared: bool = False,
97 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
98 locals: t.Optional[t.Mapping[str, t.Any]] = None,
99) -> "Context":
100 """Internal helper for context creation."""
101 if vars is None:
102 vars = {}
103 if shared:
104 parent = vars
105 else:
106 parent = dict(globals or (), **vars)
107 if locals:
108 # if the parent is shared a copy should be created because
109 # we don't want to modify the dict passed
110 if shared:
111 parent = dict(parent)
112 for key, value in locals.items():
113 if value is not missing:
114 parent[key] = value
115 return environment.context_class(
116 environment, parent, template_name, blocks, globals=globals
117 )
120class TemplateReference:
121 """The `self` in templates."""
123 def __init__(self, context: "Context") -> None:
124 self.__context = context
126 def __getitem__(self, name: str) -> t.Any:
127 blocks = self.__context.blocks[name]
128 return BlockReference(name, self.__context, blocks, 0)
130 def __repr__(self) -> str:
131 return f"<{type(self).__name__} {self.__context.name!r}>"
134def _dict_method_all(dict_method: F) -> F:
135 @functools.wraps(dict_method)
136 def f_all(self: "Context") -> t.Any:
137 return dict_method(self.get_all())
139 return t.cast(F, f_all)
142@abc.Mapping.register
143class Context:
144 """The template context holds the variables of a template. It stores the
145 values passed to the template and also the names the template exports.
146 Creating instances is neither supported nor useful as it's created
147 automatically at various stages of the template evaluation and should not
148 be created by hand.
150 The context is immutable. Modifications on :attr:`parent` **must not**
151 happen and modifications on :attr:`vars` are allowed from generated
152 template code only. Template filters and global functions marked as
153 :func:`pass_context` get the active context passed as first argument
154 and are allowed to access the context read-only.
156 The template context supports read only dict operations (`get`,
157 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
158 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
159 method that doesn't fail with a `KeyError` but returns an
160 :class:`Undefined` object for missing variables.
161 """
163 def __init__(
164 self,
165 environment: "Environment",
166 parent: t.Dict[str, t.Any],
167 name: t.Optional[str],
168 blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
169 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
170 ):
171 self.parent = parent
172 self.vars: t.Dict[str, t.Any] = {}
173 self.environment: "Environment" = environment
174 self.eval_ctx = EvalContext(self.environment, name)
175 self.exported_vars: t.Set[str] = set()
176 self.name = name
177 self.globals_keys = set() if globals is None else set(globals)
179 # create the initial mapping of blocks. Whenever template inheritance
180 # takes place the runtime will update this mapping with the new blocks
181 # from the template.
182 self.blocks = {k: [v] for k, v in blocks.items()}
184 def super(
185 self, name: str, current: t.Callable[["Context"], t.Iterator[str]]
186 ) -> t.Union["BlockReference", "Undefined"]:
187 """Render a parent block."""
188 try:
189 blocks = self.blocks[name]
190 index = blocks.index(current) + 1
191 blocks[index]
192 except LookupError:
193 return self.environment.undefined(
194 f"there is no parent block called {name!r}.", name="super"
195 )
196 return BlockReference(name, self, blocks, index)
198 def get(self, key: str, default: t.Any = None) -> t.Any:
199 """Look up a variable by name, or return a default if the key is
200 not found.
202 :param key: The variable name to look up.
203 :param default: The value to return if the key is not found.
204 """
205 try:
206 return self[key]
207 except KeyError:
208 return default
210 def resolve(self, key: str) -> t.Union[t.Any, "Undefined"]:
211 """Look up a variable by name, or return an :class:`Undefined`
212 object if the key is not found.
214 If you need to add custom behavior, override
215 :meth:`resolve_or_missing`, not this method. The various lookup
216 functions use that method, not this one.
218 :param key: The variable name to look up.
219 """
220 rv = self.resolve_or_missing(key)
222 if rv is missing:
223 return self.environment.undefined(name=key)
225 return rv
227 def resolve_or_missing(self, key: str) -> t.Any:
228 """Look up a variable by name, or return a ``missing`` sentinel
229 if the key is not found.
231 Override this method to add custom lookup behavior.
232 :meth:`resolve`, :meth:`get`, and :meth:`__getitem__` use this
233 method. Don't call this method directly.
235 :param key: The variable name to look up.
236 """
237 if key in self.vars:
238 return self.vars[key]
240 if key in self.parent:
241 return self.parent[key]
243 return missing
245 def get_exported(self) -> t.Dict[str, t.Any]:
246 """Get a new dict with the exported variables."""
247 return {k: self.vars[k] for k in self.exported_vars}
249 def get_all(self) -> t.Dict[str, t.Any]:
250 """Return the complete context as dict including the exported
251 variables. For optimizations reasons this might not return an
252 actual copy so be careful with using it.
253 """
254 if not self.vars:
255 return self.parent
256 if not self.parent:
257 return self.vars
258 return dict(self.parent, **self.vars)
260 @internalcode
261 def call(
262 __self, # noqa: B902
263 __obj: t.Callable[..., t.Any],
264 *args: t.Any,
265 **kwargs: t.Any,
266 ) -> t.Union[t.Any, "Undefined"]:
267 """Call the callable with the arguments and keyword arguments
268 provided but inject the active context or environment as first
269 argument if the callable has :func:`pass_context` or
270 :func:`pass_environment`.
271 """
272 if __debug__:
273 __traceback_hide__ = True # noqa
275 # Allow callable classes to take a context
276 if (
277 hasattr(__obj, "__call__") # noqa: B004
278 and _PassArg.from_obj(__obj.__call__) is not None
279 ):
280 __obj = __obj.__call__
282 pass_arg = _PassArg.from_obj(__obj)
284 if pass_arg is _PassArg.context:
285 # the active context should have access to variables set in
286 # loops and blocks without mutating the context itself
287 if kwargs.get("_loop_vars"):
288 __self = __self.derived(kwargs["_loop_vars"])
289 if kwargs.get("_block_vars"):
290 __self = __self.derived(kwargs["_block_vars"])
291 args = (__self,) + args
292 elif pass_arg is _PassArg.eval_context:
293 args = (__self.eval_ctx,) + args
294 elif pass_arg is _PassArg.environment:
295 args = (__self.environment,) + args
297 kwargs.pop("_block_vars", None)
298 kwargs.pop("_loop_vars", None)
300 try:
301 return __obj(*args, **kwargs)
302 except StopIteration:
303 return __self.environment.undefined(
304 "value was undefined because a callable raised a"
305 " StopIteration exception"
306 )
308 def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context":
309 """Internal helper function to create a derived context. This is
310 used in situations where the system needs a new context in the same
311 template that is independent.
312 """
313 context = new_context(
314 self.environment, self.name, {}, self.get_all(), True, None, locals
315 )
316 context.eval_ctx = self.eval_ctx
317 context.blocks.update((k, list(v)) for k, v in self.blocks.items())
318 return context
320 keys = _dict_method_all(dict.keys)
321 values = _dict_method_all(dict.values)
322 items = _dict_method_all(dict.items)
324 def __contains__(self, name: str) -> bool:
325 return name in self.vars or name in self.parent
327 def __getitem__(self, key: str) -> t.Any:
328 """Look up a variable by name with ``[]`` syntax, or raise a
329 ``KeyError`` if the key is not found.
330 """
331 item = self.resolve_or_missing(key)
333 if item is missing:
334 raise KeyError(key)
336 return item
338 def __repr__(self) -> str:
339 return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>"
342class BlockReference:
343 """One block on a template reference."""
345 def __init__(
346 self,
347 name: str,
348 context: "Context",
349 stack: t.List[t.Callable[["Context"], t.Iterator[str]]],
350 depth: int,
351 ) -> None:
352 self.name = name
353 self._context = context
354 self._stack = stack
355 self._depth = depth
357 @property
358 def super(self) -> t.Union["BlockReference", "Undefined"]:
359 """Super the block."""
360 if self._depth + 1 >= len(self._stack):
361 return self._context.environment.undefined(
362 f"there is no parent block called {self.name!r}.", name="super"
363 )
364 return BlockReference(self.name, self._context, self._stack, self._depth + 1)
366 @internalcode
367 async def _async_call(self) -> str:
368 rv = concat(
369 [x async for x in self._stack[self._depth](self._context)] # type: ignore
370 )
372 if self._context.eval_ctx.autoescape:
373 return Markup(rv)
375 return rv
377 @internalcode
378 def __call__(self) -> str:
379 if self._context.environment.is_async:
380 return self._async_call() # type: ignore
382 rv = concat(self._stack[self._depth](self._context))
384 if self._context.eval_ctx.autoescape:
385 return Markup(rv)
387 return rv
390class LoopContext:
391 """A wrapper iterable for dynamic ``for`` loops, with information
392 about the loop and iteration.
393 """
395 #: Current iteration of the loop, starting at 0.
396 index0 = -1
398 _length: t.Optional[int] = None
399 _after: t.Any = missing
400 _current: t.Any = missing
401 _before: t.Any = missing
402 _last_changed_value: t.Any = missing
404 def __init__(
405 self,
406 iterable: t.Iterable[V],
407 undefined: t.Type["Undefined"],
408 recurse: t.Optional["LoopRenderFunc"] = None,
409 depth0: int = 0,
410 ) -> None:
411 """
412 :param iterable: Iterable to wrap.
413 :param undefined: :class:`Undefined` class to use for next and
414 previous items.
415 :param recurse: The function to render the loop body when the
416 loop is marked recursive.
417 :param depth0: Incremented when looping recursively.
418 """
419 self._iterable = iterable
420 self._iterator = self._to_iterator(iterable)
421 self._undefined = undefined
422 self._recurse = recurse
423 #: How many levels deep a recursive loop currently is, starting at 0.
424 self.depth0 = depth0
426 @staticmethod
427 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
428 return iter(iterable)
430 @property
431 def length(self) -> int:
432 """Length of the iterable.
434 If the iterable is a generator or otherwise does not have a
435 size, it is eagerly evaluated to get a size.
436 """
437 if self._length is not None:
438 return self._length
440 try:
441 self._length = len(self._iterable) # type: ignore
442 except TypeError:
443 iterable = list(self._iterator)
444 self._iterator = self._to_iterator(iterable)
445 self._length = len(iterable) + self.index + (self._after is not missing)
447 return self._length
449 def __len__(self) -> int:
450 return self.length
452 @property
453 def depth(self) -> int:
454 """How many levels deep a recursive loop currently is, starting at 1."""
455 return self.depth0 + 1
457 @property
458 def index(self) -> int:
459 """Current iteration of the loop, starting at 1."""
460 return self.index0 + 1
462 @property
463 def revindex0(self) -> int:
464 """Number of iterations from the end of the loop, ending at 0.
466 Requires calculating :attr:`length`.
467 """
468 return self.length - self.index
470 @property
471 def revindex(self) -> int:
472 """Number of iterations from the end of the loop, ending at 1.
474 Requires calculating :attr:`length`.
475 """
476 return self.length - self.index0
478 @property
479 def first(self) -> bool:
480 """Whether this is the first iteration of the loop."""
481 return self.index0 == 0
483 def _peek_next(self) -> t.Any:
484 """Return the next element in the iterable, or :data:`missing`
485 if the iterable is exhausted. Only peeks one item ahead, caching
486 the result in :attr:`_last` for use in subsequent checks. The
487 cache is reset when :meth:`__next__` is called.
488 """
489 if self._after is not missing:
490 return self._after
492 self._after = next(self._iterator, missing)
493 return self._after
495 @property
496 def last(self) -> bool:
497 """Whether this is the last iteration of the loop.
499 Causes the iterable to advance early. See
500 :func:`itertools.groupby` for issues this can cause.
501 The :func:`groupby` filter avoids that issue.
502 """
503 return self._peek_next() is missing
505 @property
506 def previtem(self) -> t.Union[t.Any, "Undefined"]:
507 """The item in the previous iteration. Undefined during the
508 first iteration.
509 """
510 if self.first:
511 return self._undefined("there is no previous item")
513 return self._before
515 @property
516 def nextitem(self) -> t.Union[t.Any, "Undefined"]:
517 """The item in the next iteration. Undefined during the last
518 iteration.
520 Causes the iterable to advance early. See
521 :func:`itertools.groupby` for issues this can cause.
522 The :func:`jinja-filters.groupby` filter avoids that issue.
523 """
524 rv = self._peek_next()
526 if rv is missing:
527 return self._undefined("there is no next item")
529 return rv
531 def cycle(self, *args: V) -> V:
532 """Return a value from the given args, cycling through based on
533 the current :attr:`index0`.
535 :param args: One or more values to cycle through.
536 """
537 if not args:
538 raise TypeError("no items for cycling given")
540 return args[self.index0 % len(args)]
542 def changed(self, *value: t.Any) -> bool:
543 """Return ``True`` if previously called with a different value
544 (including when called for the first time).
546 :param value: One or more values to compare to the last call.
547 """
548 if self._last_changed_value != value:
549 self._last_changed_value = value
550 return True
552 return False
554 def __iter__(self) -> "LoopContext":
555 return self
557 def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
558 if self._after is not missing:
559 rv = self._after
560 self._after = missing
561 else:
562 rv = next(self._iterator)
564 self.index0 += 1
565 self._before = self._current
566 self._current = rv
567 return rv, self
569 @internalcode
570 def __call__(self, iterable: t.Iterable[V]) -> str:
571 """When iterating over nested data, render the body of the loop
572 recursively with the given inner iterable data.
574 The loop must have the ``recursive`` marker for this to work.
575 """
576 if self._recurse is None:
577 raise TypeError(
578 "The loop must have the 'recursive' marker to be called recursively."
579 )
581 return self._recurse(iterable, self._recurse, depth=self.depth)
583 def __repr__(self) -> str:
584 return f"<{type(self).__name__} {self.index}/{self.length}>"
587class AsyncLoopContext(LoopContext):
588 _iterator: t.AsyncIterator[t.Any] # type: ignore
590 @staticmethod
591 def _to_iterator( # type: ignore
592 iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]]
593 ) -> t.AsyncIterator[V]:
594 return auto_aiter(iterable)
596 @property
597 async def length(self) -> int: # type: ignore
598 if self._length is not None:
599 return self._length
601 try:
602 self._length = len(self._iterable) # type: ignore
603 except TypeError:
604 iterable = [x async for x in self._iterator]
605 self._iterator = self._to_iterator(iterable)
606 self._length = len(iterable) + self.index + (self._after is not missing)
608 return self._length
610 @property
611 async def revindex0(self) -> int: # type: ignore
612 return await self.length - self.index
614 @property
615 async def revindex(self) -> int: # type: ignore
616 return await self.length - self.index0
618 async def _peek_next(self) -> t.Any:
619 if self._after is not missing:
620 return self._after
622 try:
623 self._after = await self._iterator.__anext__()
624 except StopAsyncIteration:
625 self._after = missing
627 return self._after
629 @property
630 async def last(self) -> bool: # type: ignore
631 return await self._peek_next() is missing
633 @property
634 async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
635 rv = await self._peek_next()
637 if rv is missing:
638 return self._undefined("there is no next item")
640 return rv
642 def __aiter__(self) -> "AsyncLoopContext":
643 return self
645 async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
646 if self._after is not missing:
647 rv = self._after
648 self._after = missing
649 else:
650 rv = await self._iterator.__anext__()
652 self.index0 += 1
653 self._before = self._current
654 self._current = rv
655 return rv, self
658class Macro:
659 """Wraps a macro function."""
661 def __init__(
662 self,
663 environment: "Environment",
664 func: t.Callable[..., str],
665 name: str,
666 arguments: t.List[str],
667 catch_kwargs: bool,
668 catch_varargs: bool,
669 caller: bool,
670 default_autoescape: t.Optional[bool] = None,
671 ):
672 self._environment = environment
673 self._func = func
674 self._argument_count = len(arguments)
675 self.name = name
676 self.arguments = arguments
677 self.catch_kwargs = catch_kwargs
678 self.catch_varargs = catch_varargs
679 self.caller = caller
680 self.explicit_caller = "caller" in arguments
682 if default_autoescape is None:
683 if callable(environment.autoescape):
684 default_autoescape = environment.autoescape(None)
685 else:
686 default_autoescape = environment.autoescape
688 self._default_autoescape = default_autoescape
690 @internalcode
691 @pass_eval_context
692 def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
693 # This requires a bit of explanation, In the past we used to
694 # decide largely based on compile-time information if a macro is
695 # safe or unsafe. While there was a volatile mode it was largely
696 # unused for deciding on escaping. This turns out to be
697 # problematic for macros because whether a macro is safe depends not
698 # on the escape mode when it was defined, but rather when it was used.
699 #
700 # Because however we export macros from the module system and
701 # there are historic callers that do not pass an eval context (and
702 # will continue to not pass one), we need to perform an instance
703 # check here.
704 #
705 # This is considered safe because an eval context is not a valid
706 # argument to callables otherwise anyway. Worst case here is
707 # that if no eval context is passed we fall back to the compile
708 # time autoescape flag.
709 if args and isinstance(args[0], EvalContext):
710 autoescape = args[0].autoescape
711 args = args[1:]
712 else:
713 autoescape = self._default_autoescape
715 # try to consume the positional arguments
716 arguments = list(args[: self._argument_count])
717 off = len(arguments)
719 # For information why this is necessary refer to the handling
720 # of caller in the `macro_body` handler in the compiler.
721 found_caller = False
723 # if the number of arguments consumed is not the number of
724 # arguments expected we start filling in keyword arguments
725 # and defaults.
726 if off != self._argument_count:
727 for name in self.arguments[len(arguments) :]:
728 try:
729 value = kwargs.pop(name)
730 except KeyError:
731 value = missing
732 if name == "caller":
733 found_caller = True
734 arguments.append(value)
735 else:
736 found_caller = self.explicit_caller
738 # it's important that the order of these arguments does not change
739 # if not also changed in the compiler's `function_scoping` method.
740 # the order is caller, keyword arguments, positional arguments!
741 if self.caller and not found_caller:
742 caller = kwargs.pop("caller", None)
743 if caller is None:
744 caller = self._environment.undefined("No caller defined", name="caller")
745 arguments.append(caller)
747 if self.catch_kwargs:
748 arguments.append(kwargs)
749 elif kwargs:
750 if "caller" in kwargs:
751 raise TypeError(
752 f"macro {self.name!r} was invoked with two values for the special"
753 " caller argument. This is most likely a bug."
754 )
755 raise TypeError(
756 f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}"
757 )
758 if self.catch_varargs:
759 arguments.append(args[self._argument_count :])
760 elif len(args) > self._argument_count:
761 raise TypeError(
762 f"macro {self.name!r} takes not more than"
763 f" {len(self.arguments)} argument(s)"
764 )
766 return self._invoke(arguments, autoescape)
768 async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
769 rv = await self._func(*arguments) # type: ignore
771 if autoescape:
772 return Markup(rv)
774 return rv # type: ignore
776 def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
777 if self._environment.is_async:
778 return self._async_invoke(arguments, autoescape) # type: ignore
780 rv = self._func(*arguments)
782 if autoescape:
783 rv = Markup(rv)
785 return rv
787 def __repr__(self) -> str:
788 name = "anonymous" if self.name is None else repr(self.name)
789 return f"<{type(self).__name__} {name}>"
792class Undefined:
793 """The default undefined type. This undefined type can be printed and
794 iterated over, but every other access will raise an :exc:`UndefinedError`:
796 >>> foo = Undefined(name='foo')
797 >>> str(foo)
798 ''
799 >>> not foo
800 True
801 >>> foo + 42
802 Traceback (most recent call last):
803 ...
804 jinja2.exceptions.UndefinedError: 'foo' is undefined
805 """
807 __slots__ = (
808 "_undefined_hint",
809 "_undefined_obj",
810 "_undefined_name",
811 "_undefined_exception",
812 )
814 def __init__(
815 self,
816 hint: t.Optional[str] = None,
817 obj: t.Any = missing,
818 name: t.Optional[str] = None,
819 exc: t.Type[TemplateRuntimeError] = UndefinedError,
820 ) -> None:
821 self._undefined_hint = hint
822 self._undefined_obj = obj
823 self._undefined_name = name
824 self._undefined_exception = exc
826 @property
827 def _undefined_message(self) -> str:
828 """Build a message about the undefined value based on how it was
829 accessed.
830 """
831 if self._undefined_hint:
832 return self._undefined_hint
834 if self._undefined_obj is missing:
835 return f"{self._undefined_name!r} is undefined"
837 if not isinstance(self._undefined_name, str):
838 return (
839 f"{object_type_repr(self._undefined_obj)} has no"
840 f" element {self._undefined_name!r}"
841 )
843 return (
844 f"{object_type_repr(self._undefined_obj)!r} has no"
845 f" attribute {self._undefined_name!r}"
846 )
848 @internalcode
849 def _fail_with_undefined_error(
850 self, *args: t.Any, **kwargs: t.Any
851 ) -> "te.NoReturn":
852 """Raise an :exc:`UndefinedError` when operations are performed
853 on the undefined value.
854 """
855 raise self._undefined_exception(self._undefined_message)
857 @internalcode
858 def __getattr__(self, name: str) -> t.Any:
859 if name[:2] == "__":
860 raise AttributeError(name)
862 return self._fail_with_undefined_error()
864 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error
865 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error
866 __truediv__ = __rtruediv__ = _fail_with_undefined_error
867 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error
868 __mod__ = __rmod__ = _fail_with_undefined_error
869 __pos__ = __neg__ = _fail_with_undefined_error
870 __call__ = __getitem__ = _fail_with_undefined_error
871 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
872 __int__ = __float__ = __complex__ = _fail_with_undefined_error
873 __pow__ = __rpow__ = _fail_with_undefined_error
875 def __eq__(self, other: t.Any) -> bool:
876 return type(self) is type(other)
878 def __ne__(self, other: t.Any) -> bool:
879 return not self.__eq__(other)
881 def __hash__(self) -> int:
882 return id(type(self))
884 def __str__(self) -> str:
885 return ""
887 def __len__(self) -> int:
888 return 0
890 def __iter__(self) -> t.Iterator[t.Any]:
891 yield from ()
893 async def __aiter__(self) -> t.AsyncIterator[t.Any]:
894 for _ in ():
895 yield
897 def __bool__(self) -> bool:
898 return False
900 def __repr__(self) -> str:
901 return "Undefined"
904def make_logging_undefined(
905 logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
906) -> t.Type[Undefined]:
907 """Given a logger object this returns a new undefined class that will
908 log certain failures. It will log iterations and printing. If no
909 logger is given a default logger is created.
911 Example::
913 logger = logging.getLogger(__name__)
914 LoggingUndefined = make_logging_undefined(
915 logger=logger,
916 base=Undefined
917 )
919 .. versionadded:: 2.8
921 :param logger: the logger to use. If not provided, a default logger
922 is created.
923 :param base: the base class to add logging functionality to. This
924 defaults to :class:`Undefined`.
925 """
926 if logger is None:
927 import logging
929 logger = logging.getLogger(__name__)
930 logger.addHandler(logging.StreamHandler(sys.stderr))
932 def _log_message(undef: Undefined) -> None:
933 logger.warning("Template variable warning: %s", undef._undefined_message)
935 class LoggingUndefined(base): # type: ignore
936 __slots__ = ()
938 def _fail_with_undefined_error( # type: ignore
939 self, *args: t.Any, **kwargs: t.Any
940 ) -> "te.NoReturn":
941 try:
942 super()._fail_with_undefined_error(*args, **kwargs)
943 except self._undefined_exception as e:
944 logger.error("Template variable error: %s", e) # type: ignore
945 raise e
947 def __str__(self) -> str:
948 _log_message(self)
949 return super().__str__() # type: ignore
951 def __iter__(self) -> t.Iterator[t.Any]:
952 _log_message(self)
953 return super().__iter__() # type: ignore
955 def __bool__(self) -> bool:
956 _log_message(self)
957 return super().__bool__() # type: ignore
959 return LoggingUndefined
962class ChainableUndefined(Undefined):
963 """An undefined that is chainable, where both ``__getattr__`` and
964 ``__getitem__`` return itself rather than raising an
965 :exc:`UndefinedError`.
967 >>> foo = ChainableUndefined(name='foo')
968 >>> str(foo.bar['baz'])
969 ''
970 >>> foo.bar['baz'] + 42
971 Traceback (most recent call last):
972 ...
973 jinja2.exceptions.UndefinedError: 'foo' is undefined
975 .. versionadded:: 2.11.0
976 """
978 __slots__ = ()
980 def __html__(self) -> str:
981 return str(self)
983 def __getattr__(self, _: str) -> "ChainableUndefined":
984 return self
986 __getitem__ = __getattr__ # type: ignore
989class DebugUndefined(Undefined):
990 """An undefined that returns the debug info when printed.
992 >>> foo = DebugUndefined(name='foo')
993 >>> str(foo)
994 '{{ foo }}'
995 >>> not foo
996 True
997 >>> foo + 42
998 Traceback (most recent call last):
999 ...
1000 jinja2.exceptions.UndefinedError: 'foo' is undefined
1001 """
1003 __slots__ = ()
1005 def __str__(self) -> str:
1006 if self._undefined_hint:
1007 message = f"undefined value printed: {self._undefined_hint}"
1009 elif self._undefined_obj is missing:
1010 message = self._undefined_name # type: ignore
1012 else:
1013 message = (
1014 f"no such element: {object_type_repr(self._undefined_obj)}"
1015 f"[{self._undefined_name!r}]"
1016 )
1018 return f"{{{{ {message} }}}}"
1021class StrictUndefined(Undefined):
1022 """An undefined that barks on print and iteration as well as boolean
1023 tests and all kinds of comparisons. In other words: you can do nothing
1024 with it except checking if it's defined using the `defined` test.
1026 >>> foo = StrictUndefined(name='foo')
1027 >>> str(foo)
1028 Traceback (most recent call last):
1029 ...
1030 jinja2.exceptions.UndefinedError: 'foo' is undefined
1031 >>> not foo
1032 Traceback (most recent call last):
1033 ...
1034 jinja2.exceptions.UndefinedError: 'foo' is undefined
1035 >>> foo + 42
1036 Traceback (most recent call last):
1037 ...
1038 jinja2.exceptions.UndefinedError: 'foo' is undefined
1039 """
1041 __slots__ = ()
1042 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
1043 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
1044 __contains__ = Undefined._fail_with_undefined_error
1047# Remove slots attributes, after the metaclass is applied they are
1048# unneeded and contain wrong data for subclasses.
1049del (
1050 Undefined.__slots__,
1051 ChainableUndefined.__slots__,
1052 DebugUndefined.__slots__,
1053 StrictUndefined.__slots__,
1054)