Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/jinja2/runtime.py: 30%
471 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
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: 29 ↛ 30line 29 didn't jump to line 30, because the condition on line 29 was never true
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, __obj: t.Callable, *args: t.Any, **kwargs: t.Any # noqa: B902
263 ) -> t.Union[t.Any, "Undefined"]:
264 """Call the callable with the arguments and keyword arguments
265 provided but inject the active context or environment as first
266 argument if the callable has :func:`pass_context` or
267 :func:`pass_environment`.
268 """
269 if __debug__:
270 __traceback_hide__ = True # noqa
272 # Allow callable classes to take a context
273 if (
274 hasattr(__obj, "__call__") # noqa: B004
275 and _PassArg.from_obj(__obj.__call__) is not None # type: ignore
276 ):
277 __obj = __obj.__call__ # type: ignore
279 pass_arg = _PassArg.from_obj(__obj)
281 if pass_arg is _PassArg.context:
282 # the active context should have access to variables set in
283 # loops and blocks without mutating the context itself
284 if kwargs.get("_loop_vars"):
285 __self = __self.derived(kwargs["_loop_vars"])
286 if kwargs.get("_block_vars"):
287 __self = __self.derived(kwargs["_block_vars"])
288 args = (__self,) + args
289 elif pass_arg is _PassArg.eval_context:
290 args = (__self.eval_ctx,) + args
291 elif pass_arg is _PassArg.environment:
292 args = (__self.environment,) + args
294 kwargs.pop("_block_vars", None)
295 kwargs.pop("_loop_vars", None)
297 try:
298 return __obj(*args, **kwargs)
299 except StopIteration:
300 return __self.environment.undefined(
301 "value was undefined because a callable raised a"
302 " StopIteration exception"
303 )
305 def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context":
306 """Internal helper function to create a derived context. This is
307 used in situations where the system needs a new context in the same
308 template that is independent.
309 """
310 context = new_context(
311 self.environment, self.name, {}, self.get_all(), True, None, locals
312 )
313 context.eval_ctx = self.eval_ctx
314 context.blocks.update((k, list(v)) for k, v in self.blocks.items())
315 return context
317 keys = _dict_method_all(dict.keys)
318 values = _dict_method_all(dict.values)
319 items = _dict_method_all(dict.items)
321 def __contains__(self, name: str) -> bool:
322 return name in self.vars or name in self.parent
324 def __getitem__(self, key: str) -> t.Any:
325 """Look up a variable by name with ``[]`` syntax, or raise a
326 ``KeyError`` if the key is not found.
327 """
328 item = self.resolve_or_missing(key)
330 if item is missing:
331 raise KeyError(key)
333 return item
335 def __repr__(self) -> str:
336 return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>"
339class BlockReference:
340 """One block on a template reference."""
342 def __init__(
343 self,
344 name: str,
345 context: "Context",
346 stack: t.List[t.Callable[["Context"], t.Iterator[str]]],
347 depth: int,
348 ) -> None:
349 self.name = name
350 self._context = context
351 self._stack = stack
352 self._depth = depth
354 @property
355 def super(self) -> t.Union["BlockReference", "Undefined"]:
356 """Super the block."""
357 if self._depth + 1 >= len(self._stack):
358 return self._context.environment.undefined(
359 f"there is no parent block called {self.name!r}.", name="super"
360 )
361 return BlockReference(self.name, self._context, self._stack, self._depth + 1)
363 @internalcode
364 async def _async_call(self) -> str:
365 rv = concat(
366 [x async for x in self._stack[self._depth](self._context)] # type: ignore
367 )
369 if self._context.eval_ctx.autoescape:
370 return Markup(rv)
372 return rv
374 @internalcode
375 def __call__(self) -> str:
376 if self._context.environment.is_async:
377 return self._async_call() # type: ignore
379 rv = concat(self._stack[self._depth](self._context))
381 if self._context.eval_ctx.autoescape:
382 return Markup(rv)
384 return rv
387class LoopContext:
388 """A wrapper iterable for dynamic ``for`` loops, with information
389 about the loop and iteration.
390 """
392 #: Current iteration of the loop, starting at 0.
393 index0 = -1
395 _length: t.Optional[int] = None
396 _after: t.Any = missing
397 _current: t.Any = missing
398 _before: t.Any = missing
399 _last_changed_value: t.Any = missing
401 def __init__(
402 self,
403 iterable: t.Iterable[V],
404 undefined: t.Type["Undefined"],
405 recurse: t.Optional["LoopRenderFunc"] = None,
406 depth0: int = 0,
407 ) -> None:
408 """
409 :param iterable: Iterable to wrap.
410 :param undefined: :class:`Undefined` class to use for next and
411 previous items.
412 :param recurse: The function to render the loop body when the
413 loop is marked recursive.
414 :param depth0: Incremented when looping recursively.
415 """
416 self._iterable = iterable
417 self._iterator = self._to_iterator(iterable)
418 self._undefined = undefined
419 self._recurse = recurse
420 #: How many levels deep a recursive loop currently is, starting at 0.
421 self.depth0 = depth0
423 @staticmethod
424 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
425 return iter(iterable)
427 @property
428 def length(self) -> int:
429 """Length of the iterable.
431 If the iterable is a generator or otherwise does not have a
432 size, it is eagerly evaluated to get a size.
433 """
434 if self._length is not None:
435 return self._length
437 try:
438 self._length = len(self._iterable) # type: ignore
439 except TypeError:
440 iterable = list(self._iterator)
441 self._iterator = self._to_iterator(iterable)
442 self._length = len(iterable) + self.index + (self._after is not missing)
444 return self._length
446 def __len__(self) -> int:
447 return self.length
449 @property
450 def depth(self) -> int:
451 """How many levels deep a recursive loop currently is, starting at 1."""
452 return self.depth0 + 1
454 @property
455 def index(self) -> int:
456 """Current iteration of the loop, starting at 1."""
457 return self.index0 + 1
459 @property
460 def revindex0(self) -> int:
461 """Number of iterations from the end of the loop, ending at 0.
463 Requires calculating :attr:`length`.
464 """
465 return self.length - self.index
467 @property
468 def revindex(self) -> int:
469 """Number of iterations from the end of the loop, ending at 1.
471 Requires calculating :attr:`length`.
472 """
473 return self.length - self.index0
475 @property
476 def first(self) -> bool:
477 """Whether this is the first iteration of the loop."""
478 return self.index0 == 0
480 def _peek_next(self) -> t.Any:
481 """Return the next element in the iterable, or :data:`missing`
482 if the iterable is exhausted. Only peeks one item ahead, caching
483 the result in :attr:`_last` for use in subsequent checks. The
484 cache is reset when :meth:`__next__` is called.
485 """
486 if self._after is not missing:
487 return self._after
489 self._after = next(self._iterator, missing)
490 return self._after
492 @property
493 def last(self) -> bool:
494 """Whether this is the last iteration of the loop.
496 Causes the iterable to advance early. See
497 :func:`itertools.groupby` for issues this can cause.
498 The :func:`groupby` filter avoids that issue.
499 """
500 return self._peek_next() is missing
502 @property
503 def previtem(self) -> t.Union[t.Any, "Undefined"]:
504 """The item in the previous iteration. Undefined during the
505 first iteration.
506 """
507 if self.first:
508 return self._undefined("there is no previous item")
510 return self._before
512 @property
513 def nextitem(self) -> t.Union[t.Any, "Undefined"]:
514 """The item in the next iteration. Undefined during the last
515 iteration.
517 Causes the iterable to advance early. See
518 :func:`itertools.groupby` for issues this can cause.
519 The :func:`jinja-filters.groupby` filter avoids that issue.
520 """
521 rv = self._peek_next()
523 if rv is missing:
524 return self._undefined("there is no next item")
526 return rv
528 def cycle(self, *args: V) -> V:
529 """Return a value from the given args, cycling through based on
530 the current :attr:`index0`.
532 :param args: One or more values to cycle through.
533 """
534 if not args:
535 raise TypeError("no items for cycling given")
537 return args[self.index0 % len(args)]
539 def changed(self, *value: t.Any) -> bool:
540 """Return ``True`` if previously called with a different value
541 (including when called for the first time).
543 :param value: One or more values to compare to the last call.
544 """
545 if self._last_changed_value != value:
546 self._last_changed_value = value
547 return True
549 return False
551 def __iter__(self) -> "LoopContext":
552 return self
554 def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
555 if self._after is not missing:
556 rv = self._after
557 self._after = missing
558 else:
559 rv = next(self._iterator)
561 self.index0 += 1
562 self._before = self._current
563 self._current = rv
564 return rv, self
566 @internalcode
567 def __call__(self, iterable: t.Iterable[V]) -> str:
568 """When iterating over nested data, render the body of the loop
569 recursively with the given inner iterable data.
571 The loop must have the ``recursive`` marker for this to work.
572 """
573 if self._recurse is None:
574 raise TypeError(
575 "The loop must have the 'recursive' marker to be called recursively."
576 )
578 return self._recurse(iterable, self._recurse, depth=self.depth)
580 def __repr__(self) -> str:
581 return f"<{type(self).__name__} {self.index}/{self.length}>"
584class AsyncLoopContext(LoopContext):
585 _iterator: t.AsyncIterator[t.Any] # type: ignore
587 @staticmethod
588 def _to_iterator( # type: ignore
589 iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]]
590 ) -> t.AsyncIterator[V]:
591 return auto_aiter(iterable)
593 @property
594 async def length(self) -> int: # type: ignore
595 if self._length is not None:
596 return self._length
598 try:
599 self._length = len(self._iterable) # type: ignore
600 except TypeError:
601 iterable = [x async for x in self._iterator]
602 self._iterator = self._to_iterator(iterable)
603 self._length = len(iterable) + self.index + (self._after is not missing)
605 return self._length
607 @property
608 async def revindex0(self) -> int: # type: ignore
609 return await self.length - self.index
611 @property
612 async def revindex(self) -> int: # type: ignore
613 return await self.length - self.index0
615 async def _peek_next(self) -> t.Any:
616 if self._after is not missing:
617 return self._after
619 try:
620 self._after = await self._iterator.__anext__()
621 except StopAsyncIteration:
622 self._after = missing
624 return self._after
626 @property
627 async def last(self) -> bool: # type: ignore
628 return await self._peek_next() is missing
630 @property
631 async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
632 rv = await self._peek_next()
634 if rv is missing:
635 return self._undefined("there is no next item")
637 return rv
639 def __aiter__(self) -> "AsyncLoopContext":
640 return self
642 async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
643 if self._after is not missing:
644 rv = self._after
645 self._after = missing
646 else:
647 rv = await self._iterator.__anext__()
649 self.index0 += 1
650 self._before = self._current
651 self._current = rv
652 return rv, self
655class Macro:
656 """Wraps a macro function."""
658 def __init__(
659 self,
660 environment: "Environment",
661 func: t.Callable[..., str],
662 name: str,
663 arguments: t.List[str],
664 catch_kwargs: bool,
665 catch_varargs: bool,
666 caller: bool,
667 default_autoescape: t.Optional[bool] = None,
668 ):
669 self._environment = environment
670 self._func = func
671 self._argument_count = len(arguments)
672 self.name = name
673 self.arguments = arguments
674 self.catch_kwargs = catch_kwargs
675 self.catch_varargs = catch_varargs
676 self.caller = caller
677 self.explicit_caller = "caller" in arguments
679 if default_autoescape is None:
680 if callable(environment.autoescape):
681 default_autoescape = environment.autoescape(None)
682 else:
683 default_autoescape = environment.autoescape
685 self._default_autoescape = default_autoescape
687 @internalcode
688 @pass_eval_context
689 def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
690 # This requires a bit of explanation, In the past we used to
691 # decide largely based on compile-time information if a macro is
692 # safe or unsafe. While there was a volatile mode it was largely
693 # unused for deciding on escaping. This turns out to be
694 # problematic for macros because whether a macro is safe depends not
695 # on the escape mode when it was defined, but rather when it was used.
696 #
697 # Because however we export macros from the module system and
698 # there are historic callers that do not pass an eval context (and
699 # will continue to not pass one), we need to perform an instance
700 # check here.
701 #
702 # This is considered safe because an eval context is not a valid
703 # argument to callables otherwise anyway. Worst case here is
704 # that if no eval context is passed we fall back to the compile
705 # time autoescape flag.
706 if args and isinstance(args[0], EvalContext):
707 autoescape = args[0].autoescape
708 args = args[1:]
709 else:
710 autoescape = self._default_autoescape
712 # try to consume the positional arguments
713 arguments = list(args[: self._argument_count])
714 off = len(arguments)
716 # For information why this is necessary refer to the handling
717 # of caller in the `macro_body` handler in the compiler.
718 found_caller = False
720 # if the number of arguments consumed is not the number of
721 # arguments expected we start filling in keyword arguments
722 # and defaults.
723 if off != self._argument_count:
724 for name in self.arguments[len(arguments) :]:
725 try:
726 value = kwargs.pop(name)
727 except KeyError:
728 value = missing
729 if name == "caller":
730 found_caller = True
731 arguments.append(value)
732 else:
733 found_caller = self.explicit_caller
735 # it's important that the order of these arguments does not change
736 # if not also changed in the compiler's `function_scoping` method.
737 # the order is caller, keyword arguments, positional arguments!
738 if self.caller and not found_caller:
739 caller = kwargs.pop("caller", None)
740 if caller is None:
741 caller = self._environment.undefined("No caller defined", name="caller")
742 arguments.append(caller)
744 if self.catch_kwargs:
745 arguments.append(kwargs)
746 elif kwargs:
747 if "caller" in kwargs:
748 raise TypeError(
749 f"macro {self.name!r} was invoked with two values for the special"
750 " caller argument. This is most likely a bug."
751 )
752 raise TypeError(
753 f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}"
754 )
755 if self.catch_varargs:
756 arguments.append(args[self._argument_count :])
757 elif len(args) > self._argument_count:
758 raise TypeError(
759 f"macro {self.name!r} takes not more than"
760 f" {len(self.arguments)} argument(s)"
761 )
763 return self._invoke(arguments, autoescape)
765 async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
766 rv = await self._func(*arguments) # type: ignore
768 if autoescape:
769 return Markup(rv)
771 return rv # type: ignore
773 def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
774 if self._environment.is_async:
775 return self._async_invoke(arguments, autoescape) # type: ignore
777 rv = self._func(*arguments)
779 if autoescape:
780 rv = Markup(rv)
782 return rv
784 def __repr__(self) -> str:
785 name = "anonymous" if self.name is None else repr(self.name)
786 return f"<{type(self).__name__} {name}>"
789class Undefined:
790 """The default undefined type. This undefined type can be printed and
791 iterated over, but every other access will raise an :exc:`UndefinedError`:
793 >>> foo = Undefined(name='foo')
794 >>> str(foo)
795 ''
796 >>> not foo
797 True
798 >>> foo + 42
799 Traceback (most recent call last):
800 ...
801 jinja2.exceptions.UndefinedError: 'foo' is undefined
802 """
804 __slots__ = (
805 "_undefined_hint",
806 "_undefined_obj",
807 "_undefined_name",
808 "_undefined_exception",
809 )
811 def __init__(
812 self,
813 hint: t.Optional[str] = None,
814 obj: t.Any = missing,
815 name: t.Optional[str] = None,
816 exc: t.Type[TemplateRuntimeError] = UndefinedError,
817 ) -> None:
818 self._undefined_hint = hint
819 self._undefined_obj = obj
820 self._undefined_name = name
821 self._undefined_exception = exc
823 @property
824 def _undefined_message(self) -> str:
825 """Build a message about the undefined value based on how it was
826 accessed.
827 """
828 if self._undefined_hint:
829 return self._undefined_hint
831 if self._undefined_obj is missing:
832 return f"{self._undefined_name!r} is undefined"
834 if not isinstance(self._undefined_name, str):
835 return (
836 f"{object_type_repr(self._undefined_obj)} has no"
837 f" element {self._undefined_name!r}"
838 )
840 return (
841 f"{object_type_repr(self._undefined_obj)!r} has no"
842 f" attribute {self._undefined_name!r}"
843 )
845 @internalcode
846 def _fail_with_undefined_error(
847 self, *args: t.Any, **kwargs: t.Any
848 ) -> "te.NoReturn":
849 """Raise an :exc:`UndefinedError` when operations are performed
850 on the undefined value.
851 """
852 raise self._undefined_exception(self._undefined_message)
854 @internalcode
855 def __getattr__(self, name: str) -> t.Any:
856 if name[:2] == "__":
857 raise AttributeError(name)
859 return self._fail_with_undefined_error()
861 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error
862 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error
863 __truediv__ = __rtruediv__ = _fail_with_undefined_error
864 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error
865 __mod__ = __rmod__ = _fail_with_undefined_error
866 __pos__ = __neg__ = _fail_with_undefined_error
867 __call__ = __getitem__ = _fail_with_undefined_error
868 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
869 __int__ = __float__ = __complex__ = _fail_with_undefined_error
870 __pow__ = __rpow__ = _fail_with_undefined_error
872 def __eq__(self, other: t.Any) -> bool:
873 return type(self) is type(other)
875 def __ne__(self, other: t.Any) -> bool:
876 return not self.__eq__(other)
878 def __hash__(self) -> int:
879 return id(type(self))
881 def __str__(self) -> str:
882 return ""
884 def __len__(self) -> int:
885 return 0
887 def __iter__(self) -> t.Iterator[t.Any]:
888 yield from ()
890 async def __aiter__(self) -> t.AsyncIterator[t.Any]:
891 for _ in ():
892 yield
894 def __bool__(self) -> bool:
895 return False
897 def __repr__(self) -> str:
898 return "Undefined"
901def make_logging_undefined(
902 logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
903) -> t.Type[Undefined]:
904 """Given a logger object this returns a new undefined class that will
905 log certain failures. It will log iterations and printing. If no
906 logger is given a default logger is created.
908 Example::
910 logger = logging.getLogger(__name__)
911 LoggingUndefined = make_logging_undefined(
912 logger=logger,
913 base=Undefined
914 )
916 .. versionadded:: 2.8
918 :param logger: the logger to use. If not provided, a default logger
919 is created.
920 :param base: the base class to add logging functionality to. This
921 defaults to :class:`Undefined`.
922 """
923 if logger is None:
924 import logging
926 logger = logging.getLogger(__name__)
927 logger.addHandler(logging.StreamHandler(sys.stderr))
929 def _log_message(undef: Undefined) -> None:
930 logger.warning( # type: ignore
931 "Template variable warning: %s", undef._undefined_message
932 )
934 class LoggingUndefined(base): # type: ignore
935 __slots__ = ()
937 def _fail_with_undefined_error( # type: ignore
938 self, *args: t.Any, **kwargs: t.Any
939 ) -> "te.NoReturn":
940 try:
941 super()._fail_with_undefined_error(*args, **kwargs)
942 except self._undefined_exception as e:
943 logger.error("Template variable error: %s", e) # type: ignore
944 raise e
946 def __str__(self) -> str:
947 _log_message(self)
948 return super().__str__() # type: ignore
950 def __iter__(self) -> t.Iterator[t.Any]:
951 _log_message(self)
952 return super().__iter__() # type: ignore
954 def __bool__(self) -> bool:
955 _log_message(self)
956 return super().__bool__() # type: ignore
958 return LoggingUndefined
961class ChainableUndefined(Undefined):
962 """An undefined that is chainable, where both ``__getattr__`` and
963 ``__getitem__`` return itself rather than raising an
964 :exc:`UndefinedError`.
966 >>> foo = ChainableUndefined(name='foo')
967 >>> str(foo.bar['baz'])
968 ''
969 >>> foo.bar['baz'] + 42
970 Traceback (most recent call last):
971 ...
972 jinja2.exceptions.UndefinedError: 'foo' is undefined
974 .. versionadded:: 2.11.0
975 """
977 __slots__ = ()
979 def __html__(self) -> str:
980 return str(self)
982 def __getattr__(self, _: str) -> "ChainableUndefined":
983 return self
985 __getitem__ = __getattr__ # type: ignore
988class DebugUndefined(Undefined):
989 """An undefined that returns the debug info when printed.
991 >>> foo = DebugUndefined(name='foo')
992 >>> str(foo)
993 '{{ foo }}'
994 >>> not foo
995 True
996 >>> foo + 42
997 Traceback (most recent call last):
998 ...
999 jinja2.exceptions.UndefinedError: 'foo' is undefined
1000 """
1002 __slots__ = ()
1004 def __str__(self) -> str:
1005 if self._undefined_hint:
1006 message = f"undefined value printed: {self._undefined_hint}"
1008 elif self._undefined_obj is missing:
1009 message = self._undefined_name # type: ignore
1011 else:
1012 message = (
1013 f"no such element: {object_type_repr(self._undefined_obj)}"
1014 f"[{self._undefined_name!r}]"
1015 )
1017 return f"{{{{ {message} }}}}"
1020class StrictUndefined(Undefined):
1021 """An undefined that barks on print and iteration as well as boolean
1022 tests and all kinds of comparisons. In other words: you can do nothing
1023 with it except checking if it's defined using the `defined` test.
1025 >>> foo = StrictUndefined(name='foo')
1026 >>> str(foo)
1027 Traceback (most recent call last):
1028 ...
1029 jinja2.exceptions.UndefinedError: 'foo' is undefined
1030 >>> not foo
1031 Traceback (most recent call last):
1032 ...
1033 jinja2.exceptions.UndefinedError: 'foo' is undefined
1034 >>> foo + 42
1035 Traceback (most recent call last):
1036 ...
1037 jinja2.exceptions.UndefinedError: 'foo' is undefined
1038 """
1040 __slots__ = ()
1041 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
1042 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
1043 __contains__ = Undefined._fail_with_undefined_error
1046# Remove slots attributes, after the metaclass is applied they are
1047# unneeded and contain wrong data for subclasses.
1048del (
1049 Undefined.__slots__,
1050 ChainableUndefined.__slots__,
1051 DebugUndefined.__slots__,
1052 StrictUndefined.__slots__,
1053)