Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/jinja2/runtime.py: 35%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""The runtime functions and state used by compiled templates."""
3import functools
4import sys
5import typing as t
6from collections import abc
7from itertools import chain
9from markupsafe import escape # noqa: F401
10from markupsafe import Markup
11from markupsafe import soft_str
13from .async_utils import auto_aiter
14from .async_utils import auto_await # noqa: F401
15from .exceptions import TemplateNotFound # noqa: F401
16from .exceptions import TemplateRuntimeError # noqa: F401
17from .exceptions import UndefinedError
18from .nodes import EvalContext
19from .utils import _PassArg
20from .utils import concat
21from .utils import internalcode
22from .utils import missing
23from .utils import Namespace # noqa: F401
24from .utils import object_type_repr
25from .utils import pass_eval_context
27V = t.TypeVar("V")
28F = t.TypeVar("F", bound=t.Callable[..., t.Any])
30if t.TYPE_CHECKING:
31 import logging
33 import typing_extensions as te
35 from .environment import Environment
37 class LoopRenderFunc(te.Protocol):
38 def __call__(
39 self,
40 reciter: t.Iterable[V],
41 loop_render_func: "LoopRenderFunc",
42 depth: int = 0,
43 ) -> str: ...
46# these variables are exported to the template runtime
47exported = [
48 "LoopContext",
49 "TemplateReference",
50 "Macro",
51 "Markup",
52 "TemplateRuntimeError",
53 "missing",
54 "escape",
55 "markup_join",
56 "str_join",
57 "identity",
58 "TemplateNotFound",
59 "Namespace",
60 "Undefined",
61 "internalcode",
62]
63async_exported = [
64 "AsyncLoopContext",
65 "auto_aiter",
66 "auto_await",
67]
70def identity(x: V) -> V:
71 """Returns its argument. Useful for certain things in the
72 environment.
73 """
74 return x
77def markup_join(seq: t.Iterable[t.Any]) -> str:
78 """Concatenation that escapes if necessary and converts to string."""
79 buf = []
80 iterator = map(soft_str, seq)
81 for arg in iterator:
82 buf.append(arg)
83 if hasattr(arg, "__html__"):
84 return Markup("").join(chain(buf, iterator))
85 return concat(buf)
88def str_join(seq: t.Iterable[t.Any]) -> str:
89 """Simple args to string conversion and concatenation."""
90 return concat(map(str, seq))
93def new_context(
94 environment: "Environment",
95 template_name: t.Optional[str],
96 blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
97 vars: t.Optional[t.Dict[str, t.Any]] = None,
98 shared: bool = False,
99 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
100 locals: t.Optional[t.Mapping[str, t.Any]] = None,
101) -> "Context":
102 """Internal helper for context creation."""
103 if vars is None:
104 vars = {}
105 if shared:
106 parent = vars
107 else:
108 parent = dict(globals or (), **vars)
109 if locals:
110 # if the parent is shared a copy should be created because
111 # we don't want to modify the dict passed
112 if shared:
113 parent = dict(parent)
114 for key, value in locals.items():
115 if value is not missing:
116 parent[key] = value
117 return environment.context_class(
118 environment, parent, template_name, blocks, globals=globals
119 )
122class TemplateReference:
123 """The `self` in templates."""
125 def __init__(self, context: "Context") -> None:
126 self.__context = context
128 def __getitem__(self, name: str) -> t.Any:
129 blocks = self.__context.blocks[name]
130 return BlockReference(name, self.__context, blocks, 0)
132 def __repr__(self) -> str:
133 return f"<{type(self).__name__} {self.__context.name!r}>"
136def _dict_method_all(dict_method: F) -> F:
137 @functools.wraps(dict_method)
138 def f_all(self: "Context") -> t.Any:
139 return dict_method(self.get_all())
141 return t.cast(F, f_all)
144@abc.Mapping.register
145class Context:
146 """The template context holds the variables of a template. It stores the
147 values passed to the template and also the names the template exports.
148 Creating instances is neither supported nor useful as it's created
149 automatically at various stages of the template evaluation and should not
150 be created by hand.
152 The context is immutable. Modifications on :attr:`parent` **must not**
153 happen and modifications on :attr:`vars` are allowed from generated
154 template code only. Template filters and global functions marked as
155 :func:`pass_context` get the active context passed as first argument
156 and are allowed to access the context read-only.
158 The template context supports read only dict operations (`get`,
159 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
160 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
161 method that doesn't fail with a `KeyError` but returns an
162 :class:`Undefined` object for missing variables.
163 """
165 def __init__(
166 self,
167 environment: "Environment",
168 parent: t.Dict[str, t.Any],
169 name: t.Optional[str],
170 blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]],
171 globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
172 ):
173 self.parent = parent
174 self.vars: t.Dict[str, t.Any] = {}
175 self.environment: Environment = environment
176 self.eval_ctx = EvalContext(self.environment, name)
177 self.exported_vars: t.Set[str] = set()
178 self.name = name
179 self.globals_keys = set() if globals is None else set(globals)
181 # create the initial mapping of blocks. Whenever template inheritance
182 # takes place the runtime will update this mapping with the new blocks
183 # from the template.
184 self.blocks = {k: [v] for k, v in blocks.items()}
186 def super(
187 self, name: str, current: t.Callable[["Context"], t.Iterator[str]]
188 ) -> t.Union["BlockReference", "Undefined"]:
189 """Render a parent block."""
190 try:
191 blocks = self.blocks[name]
192 index = blocks.index(current) + 1
193 blocks[index]
194 except LookupError:
195 return self.environment.undefined(
196 f"there is no parent block called {name!r}.", name="super"
197 )
198 return BlockReference(name, self, blocks, index)
200 def get(self, key: str, default: t.Any = None) -> t.Any:
201 """Look up a variable by name, or return a default if the key is
202 not found.
204 :param key: The variable name to look up.
205 :param default: The value to return if the key is not found.
206 """
207 try:
208 return self[key]
209 except KeyError:
210 return default
212 def resolve(self, key: str) -> t.Union[t.Any, "Undefined"]:
213 """Look up a variable by name, or return an :class:`Undefined`
214 object if the key is not found.
216 If you need to add custom behavior, override
217 :meth:`resolve_or_missing`, not this method. The various lookup
218 functions use that method, not this one.
220 :param key: The variable name to look up.
221 """
222 rv = self.resolve_or_missing(key)
224 if rv is missing:
225 return self.environment.undefined(name=key)
227 return rv
229 def resolve_or_missing(self, key: str) -> t.Any:
230 """Look up a variable by name, or return a ``missing`` sentinel
231 if the key is not found.
233 Override this method to add custom lookup behavior.
234 :meth:`resolve`, :meth:`get`, and :meth:`__getitem__` use this
235 method. Don't call this method directly.
237 :param key: The variable name to look up.
238 """
239 if key in self.vars:
240 return self.vars[key]
242 if key in self.parent:
243 return self.parent[key]
245 return missing
247 def get_exported(self) -> t.Dict[str, t.Any]:
248 """Get a new dict with the exported variables."""
249 return {k: self.vars[k] for k in self.exported_vars}
251 def get_all(self) -> t.Dict[str, t.Any]:
252 """Return the complete context as dict including the exported
253 variables. For optimizations reasons this might not return an
254 actual copy so be careful with using it.
255 """
256 if not self.vars:
257 return self.parent
258 if not self.parent:
259 return self.vars
260 return dict(self.parent, **self.vars)
262 @internalcode
263 def call(
264 __self,
265 __obj: t.Callable[..., t.Any],
266 *args: t.Any,
267 **kwargs: t.Any, # noqa: B902
268 ) -> t.Union[t.Any, "Undefined"]:
269 """Call the callable with the arguments and keyword arguments
270 provided but inject the active context or environment as first
271 argument if the callable has :func:`pass_context` or
272 :func:`pass_environment`.
273 """
274 if __debug__:
275 __traceback_hide__ = True # noqa
277 # Allow callable classes to take a context
278 if (
279 hasattr(__obj, "__call__") # noqa: B004
280 and _PassArg.from_obj(__obj.__call__) is not None
281 ):
282 __obj = __obj.__call__
284 pass_arg = _PassArg.from_obj(__obj)
286 if pass_arg is _PassArg.context:
287 # the active context should have access to variables set in
288 # loops and blocks without mutating the context itself
289 if kwargs.get("_loop_vars"):
290 __self = __self.derived(kwargs["_loop_vars"])
291 if kwargs.get("_block_vars"):
292 __self = __self.derived(kwargs["_block_vars"])
293 args = (__self,) + args
294 elif pass_arg is _PassArg.eval_context:
295 args = (__self.eval_ctx,) + args
296 elif pass_arg is _PassArg.environment:
297 args = (__self.environment,) + args
299 kwargs.pop("_block_vars", None)
300 kwargs.pop("_loop_vars", None)
302 try:
303 return __obj(*args, **kwargs)
304 except StopIteration:
305 return __self.environment.undefined(
306 "value was undefined because a callable raised a"
307 " StopIteration exception"
308 )
310 def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context":
311 """Internal helper function to create a derived context. This is
312 used in situations where the system needs a new context in the same
313 template that is independent.
314 """
315 context = new_context(
316 self.environment, self.name, {}, self.get_all(), True, None, locals
317 )
318 context.eval_ctx = self.eval_ctx
319 context.blocks.update((k, list(v)) for k, v in self.blocks.items())
320 return context
322 keys = _dict_method_all(dict.keys)
323 values = _dict_method_all(dict.values)
324 items = _dict_method_all(dict.items)
326 def __contains__(self, name: str) -> bool:
327 return name in self.vars or name in self.parent
329 def __getitem__(self, key: str) -> t.Any:
330 """Look up a variable by name with ``[]`` syntax, or raise a
331 ``KeyError`` if the key is not found.
332 """
333 item = self.resolve_or_missing(key)
335 if item is missing:
336 raise KeyError(key)
338 return item
340 def __repr__(self) -> str:
341 return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>"
344class BlockReference:
345 """One block on a template reference."""
347 def __init__(
348 self,
349 name: str,
350 context: "Context",
351 stack: t.List[t.Callable[["Context"], t.Iterator[str]]],
352 depth: int,
353 ) -> None:
354 self.name = name
355 self._context = context
356 self._stack = stack
357 self._depth = depth
359 @property
360 def super(self) -> t.Union["BlockReference", "Undefined"]:
361 """Super the block."""
362 if self._depth + 1 >= len(self._stack):
363 return self._context.environment.undefined(
364 f"there is no parent block called {self.name!r}.", name="super"
365 )
366 return BlockReference(self.name, self._context, self._stack, self._depth + 1)
368 @internalcode
369 async def _async_call(self) -> str:
370 rv = self._context.environment.concat( # type: ignore
371 [x async for x in self._stack[self._depth](self._context)] # type: ignore
372 )
374 if self._context.eval_ctx.autoescape:
375 return Markup(rv)
377 return rv
379 @internalcode
380 def __call__(self) -> str:
381 if self._context.environment.is_async:
382 return self._async_call() # type: ignore
384 rv = self._context.environment.concat( # type: ignore
385 self._stack[self._depth](self._context)
386 )
388 if self._context.eval_ctx.autoescape:
389 return Markup(rv)
391 return rv
394class LoopContext:
395 """A wrapper iterable for dynamic ``for`` loops, with information
396 about the loop and iteration.
397 """
399 #: Current iteration of the loop, starting at 0.
400 index0 = -1
402 _length: t.Optional[int] = None
403 _after: t.Any = missing
404 _current: t.Any = missing
405 _before: t.Any = missing
406 _last_changed_value: t.Any = missing
408 def __init__(
409 self,
410 iterable: t.Iterable[V],
411 undefined: t.Type["Undefined"],
412 recurse: t.Optional["LoopRenderFunc"] = None,
413 depth0: int = 0,
414 ) -> None:
415 """
416 :param iterable: Iterable to wrap.
417 :param undefined: :class:`Undefined` class to use for next and
418 previous items.
419 :param recurse: The function to render the loop body when the
420 loop is marked recursive.
421 :param depth0: Incremented when looping recursively.
422 """
423 self._iterable = iterable
424 self._iterator = self._to_iterator(iterable)
425 self._undefined = undefined
426 self._recurse = recurse
427 #: How many levels deep a recursive loop currently is, starting at 0.
428 self.depth0 = depth0
430 @staticmethod
431 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
432 return iter(iterable)
434 @property
435 def length(self) -> int:
436 """Length of the iterable.
438 If the iterable is a generator or otherwise does not have a
439 size, it is eagerly evaluated to get a size.
440 """
441 if self._length is not None:
442 return self._length
444 try:
445 self._length = len(self._iterable) # type: ignore
446 except TypeError:
447 iterable = list(self._iterator)
448 self._iterator = self._to_iterator(iterable)
449 self._length = len(iterable) + self.index + (self._after is not missing)
451 return self._length
453 def __len__(self) -> int:
454 return self.length
456 @property
457 def depth(self) -> int:
458 """How many levels deep a recursive loop currently is, starting at 1."""
459 return self.depth0 + 1
461 @property
462 def index(self) -> int:
463 """Current iteration of the loop, starting at 1."""
464 return self.index0 + 1
466 @property
467 def revindex0(self) -> int:
468 """Number of iterations from the end of the loop, ending at 0.
470 Requires calculating :attr:`length`.
471 """
472 return self.length - self.index
474 @property
475 def revindex(self) -> int:
476 """Number of iterations from the end of the loop, ending at 1.
478 Requires calculating :attr:`length`.
479 """
480 return self.length - self.index0
482 @property
483 def first(self) -> bool:
484 """Whether this is the first iteration of the loop."""
485 return self.index0 == 0
487 def _peek_next(self) -> t.Any:
488 """Return the next element in the iterable, or :data:`missing`
489 if the iterable is exhausted. Only peeks one item ahead, caching
490 the result in :attr:`_last` for use in subsequent checks. The
491 cache is reset when :meth:`__next__` is called.
492 """
493 if self._after is not missing:
494 return self._after
496 self._after = next(self._iterator, missing)
497 return self._after
499 @property
500 def last(self) -> bool:
501 """Whether this is the last iteration of the loop.
503 Causes the iterable to advance early. See
504 :func:`itertools.groupby` for issues this can cause.
505 The :func:`groupby` filter avoids that issue.
506 """
507 return self._peek_next() is missing
509 @property
510 def previtem(self) -> t.Union[t.Any, "Undefined"]:
511 """The item in the previous iteration. Undefined during the
512 first iteration.
513 """
514 if self.first:
515 return self._undefined("there is no previous item")
517 return self._before
519 @property
520 def nextitem(self) -> t.Union[t.Any, "Undefined"]:
521 """The item in the next iteration. Undefined during the last
522 iteration.
524 Causes the iterable to advance early. See
525 :func:`itertools.groupby` for issues this can cause.
526 The :func:`jinja-filters.groupby` filter avoids that issue.
527 """
528 rv = self._peek_next()
530 if rv is missing:
531 return self._undefined("there is no next item")
533 return rv
535 def cycle(self, *args: V) -> V:
536 """Return a value from the given args, cycling through based on
537 the current :attr:`index0`.
539 :param args: One or more values to cycle through.
540 """
541 if not args:
542 raise TypeError("no items for cycling given")
544 return args[self.index0 % len(args)]
546 def changed(self, *value: t.Any) -> bool:
547 """Return ``True`` if previously called with a different value
548 (including when called for the first time).
550 :param value: One or more values to compare to the last call.
551 """
552 if self._last_changed_value != value:
553 self._last_changed_value = value
554 return True
556 return False
558 def __iter__(self) -> "LoopContext":
559 return self
561 def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
562 if self._after is not missing:
563 rv = self._after
564 self._after = missing
565 else:
566 rv = next(self._iterator)
568 self.index0 += 1
569 self._before = self._current
570 self._current = rv
571 return rv, self
573 @internalcode
574 def __call__(self, iterable: t.Iterable[V]) -> str:
575 """When iterating over nested data, render the body of the loop
576 recursively with the given inner iterable data.
578 The loop must have the ``recursive`` marker for this to work.
579 """
580 if self._recurse is None:
581 raise TypeError(
582 "The loop must have the 'recursive' marker to be called recursively."
583 )
585 return self._recurse(iterable, self._recurse, depth=self.depth)
587 def __repr__(self) -> str:
588 return f"<{type(self).__name__} {self.index}/{self.length}>"
591class AsyncLoopContext(LoopContext):
592 _iterator: t.AsyncIterator[t.Any] # type: ignore
594 @staticmethod
595 def _to_iterator( # type: ignore
596 iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]],
597 ) -> t.AsyncIterator[V]:
598 return auto_aiter(iterable)
600 @property
601 async def length(self) -> int: # type: ignore
602 if self._length is not None:
603 return self._length
605 try:
606 self._length = len(self._iterable) # type: ignore
607 except TypeError:
608 iterable = [x async for x in self._iterator]
609 self._iterator = self._to_iterator(iterable)
610 self._length = len(iterable) + self.index + (self._after is not missing)
612 return self._length
614 @property
615 async def revindex0(self) -> int: # type: ignore
616 return await self.length - self.index
618 @property
619 async def revindex(self) -> int: # type: ignore
620 return await self.length - self.index0
622 async def _peek_next(self) -> t.Any:
623 if self._after is not missing:
624 return self._after
626 try:
627 self._after = await self._iterator.__anext__()
628 except StopAsyncIteration:
629 self._after = missing
631 return self._after
633 @property
634 async def last(self) -> bool: # type: ignore
635 return await self._peek_next() is missing
637 @property
638 async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
639 rv = await self._peek_next()
641 if rv is missing:
642 return self._undefined("there is no next item")
644 return rv
646 def __aiter__(self) -> "AsyncLoopContext":
647 return self
649 async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
650 if self._after is not missing:
651 rv = self._after
652 self._after = missing
653 else:
654 rv = await self._iterator.__anext__()
656 self.index0 += 1
657 self._before = self._current
658 self._current = rv
659 return rv, self
662class Macro:
663 """Wraps a macro function."""
665 def __init__(
666 self,
667 environment: "Environment",
668 func: t.Callable[..., str],
669 name: str,
670 arguments: t.List[str],
671 catch_kwargs: bool,
672 catch_varargs: bool,
673 caller: bool,
674 default_autoescape: t.Optional[bool] = None,
675 ):
676 self._environment = environment
677 self._func = func
678 self._argument_count = len(arguments)
679 self.name = name
680 self.arguments = arguments
681 self.catch_kwargs = catch_kwargs
682 self.catch_varargs = catch_varargs
683 self.caller = caller
684 self.explicit_caller = "caller" in arguments
686 if default_autoescape is None:
687 if callable(environment.autoescape):
688 default_autoescape = environment.autoescape(None)
689 else:
690 default_autoescape = environment.autoescape
692 self._default_autoescape = default_autoescape
694 @internalcode
695 @pass_eval_context
696 def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
697 # This requires a bit of explanation, In the past we used to
698 # decide largely based on compile-time information if a macro is
699 # safe or unsafe. While there was a volatile mode it was largely
700 # unused for deciding on escaping. This turns out to be
701 # problematic for macros because whether a macro is safe depends not
702 # on the escape mode when it was defined, but rather when it was used.
703 #
704 # Because however we export macros from the module system and
705 # there are historic callers that do not pass an eval context (and
706 # will continue to not pass one), we need to perform an instance
707 # check here.
708 #
709 # This is considered safe because an eval context is not a valid
710 # argument to callables otherwise anyway. Worst case here is
711 # that if no eval context is passed we fall back to the compile
712 # time autoescape flag.
713 if args and isinstance(args[0], EvalContext):
714 autoescape = args[0].autoescape
715 args = args[1:]
716 else:
717 autoescape = self._default_autoescape
719 # try to consume the positional arguments
720 arguments = list(args[: self._argument_count])
721 off = len(arguments)
723 # For information why this is necessary refer to the handling
724 # of caller in the `macro_body` handler in the compiler.
725 found_caller = False
727 # if the number of arguments consumed is not the number of
728 # arguments expected we start filling in keyword arguments
729 # and defaults.
730 if off != self._argument_count:
731 for name in self.arguments[len(arguments) :]:
732 try:
733 value = kwargs.pop(name)
734 except KeyError:
735 value = missing
736 if name == "caller":
737 found_caller = True
738 arguments.append(value)
739 else:
740 found_caller = self.explicit_caller
742 # it's important that the order of these arguments does not change
743 # if not also changed in the compiler's `function_scoping` method.
744 # the order is caller, keyword arguments, positional arguments!
745 if self.caller and not found_caller:
746 caller = kwargs.pop("caller", None)
747 if caller is None:
748 caller = self._environment.undefined("No caller defined", name="caller")
749 arguments.append(caller)
751 if self.catch_kwargs:
752 arguments.append(kwargs)
753 elif kwargs:
754 if "caller" in kwargs:
755 raise TypeError(
756 f"macro {self.name!r} was invoked with two values for the special"
757 " caller argument. This is most likely a bug."
758 )
759 raise TypeError(
760 f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}"
761 )
762 if self.catch_varargs:
763 arguments.append(args[self._argument_count :])
764 elif len(args) > self._argument_count:
765 raise TypeError(
766 f"macro {self.name!r} takes not more than"
767 f" {len(self.arguments)} argument(s)"
768 )
770 return self._invoke(arguments, autoescape)
772 async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
773 rv = await self._func(*arguments) # type: ignore
775 if autoescape:
776 return Markup(rv)
778 return rv # type: ignore
780 def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
781 if self._environment.is_async:
782 return self._async_invoke(arguments, autoescape) # type: ignore
784 rv = self._func(*arguments)
786 if autoescape:
787 rv = Markup(rv)
789 return rv
791 def __repr__(self) -> str:
792 name = "anonymous" if self.name is None else repr(self.name)
793 return f"<{type(self).__name__} {name}>"
796class Undefined:
797 """The default undefined type. This can be printed, iterated, and treated as
798 a boolean. Any other operation will raise an :exc:`UndefinedError`.
800 >>> foo = Undefined(name='foo')
801 >>> str(foo)
802 ''
803 >>> not foo
804 True
805 >>> foo + 42
806 Traceback (most recent call last):
807 ...
808 jinja2.exceptions.UndefinedError: 'foo' is undefined
809 """
811 __slots__ = (
812 "_undefined_hint",
813 "_undefined_obj",
814 "_undefined_name",
815 "_undefined_exception",
816 )
818 def __init__(
819 self,
820 hint: t.Optional[str] = None,
821 obj: t.Any = missing,
822 name: t.Optional[str] = None,
823 exc: t.Type[TemplateRuntimeError] = UndefinedError,
824 ) -> None:
825 self._undefined_hint = hint
826 self._undefined_obj = obj
827 self._undefined_name = name
828 self._undefined_exception = exc
830 @property
831 def _undefined_message(self) -> str:
832 """Build a message about the undefined value based on how it was
833 accessed.
834 """
835 if self._undefined_hint:
836 return self._undefined_hint
838 if self._undefined_obj is missing:
839 return f"{self._undefined_name!r} is undefined"
841 if not isinstance(self._undefined_name, str):
842 return (
843 f"{object_type_repr(self._undefined_obj)} has no"
844 f" element {self._undefined_name!r}"
845 )
847 return (
848 f"{object_type_repr(self._undefined_obj)!r} has no"
849 f" attribute {self._undefined_name!r}"
850 )
852 @internalcode
853 def _fail_with_undefined_error(
854 self, *args: t.Any, **kwargs: t.Any
855 ) -> "te.NoReturn":
856 """Raise an :exc:`UndefinedError` when operations are performed
857 on the undefined value.
858 """
859 raise self._undefined_exception(self._undefined_message)
861 @internalcode
862 def __getattr__(self, name: str) -> t.Any:
863 # Raise AttributeError on requests for names that appear to be unimplemented
864 # dunder methods to keep Python's internal protocol probing behaviors working
865 # properly in cases where another exception type could cause unexpected or
866 # difficult-to-diagnose failures.
867 if name[:2] == "__" and name[-2:] == "__":
868 raise AttributeError(name)
870 return self._fail_with_undefined_error()
872 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error
873 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error
874 __truediv__ = __rtruediv__ = _fail_with_undefined_error
875 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error
876 __mod__ = __rmod__ = _fail_with_undefined_error
877 __pos__ = __neg__ = _fail_with_undefined_error
878 __call__ = __getitem__ = _fail_with_undefined_error
879 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
880 __int__ = __float__ = __complex__ = _fail_with_undefined_error
881 __pow__ = __rpow__ = _fail_with_undefined_error
883 def __eq__(self, other: t.Any) -> bool:
884 return type(self) is type(other)
886 def __ne__(self, other: t.Any) -> bool:
887 return not self.__eq__(other)
889 def __hash__(self) -> int:
890 return id(type(self))
892 def __str__(self) -> str:
893 return ""
895 def __len__(self) -> int:
896 return 0
898 def __iter__(self) -> t.Iterator[t.Any]:
899 yield from ()
901 async def __aiter__(self) -> t.AsyncIterator[t.Any]:
902 for _ in ():
903 yield
905 def __bool__(self) -> bool:
906 return False
908 def __repr__(self) -> str:
909 return "Undefined"
912def make_logging_undefined(
913 logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
914) -> t.Type[Undefined]:
915 """Given a logger object this returns a new undefined class that will
916 log certain failures. It will log iterations and printing. If no
917 logger is given a default logger is created.
919 Example::
921 logger = logging.getLogger(__name__)
922 LoggingUndefined = make_logging_undefined(
923 logger=logger,
924 base=Undefined
925 )
927 .. versionadded:: 2.8
929 :param logger: the logger to use. If not provided, a default logger
930 is created.
931 :param base: the base class to add logging functionality to. This
932 defaults to :class:`Undefined`.
933 """
934 if logger is None:
935 import logging
937 logger = logging.getLogger(__name__)
938 logger.addHandler(logging.StreamHandler(sys.stderr))
940 def _log_message(undef: Undefined) -> None:
941 logger.warning("Template variable warning: %s", undef._undefined_message)
943 class LoggingUndefined(base): # type: ignore
944 __slots__ = ()
946 def _fail_with_undefined_error( # type: ignore
947 self, *args: t.Any, **kwargs: t.Any
948 ) -> "te.NoReturn":
949 try:
950 super()._fail_with_undefined_error(*args, **kwargs)
951 except self._undefined_exception as e:
952 logger.error("Template variable error: %s", e) # type: ignore
953 raise e
955 def __str__(self) -> str:
956 _log_message(self)
957 return super().__str__() # type: ignore
959 def __iter__(self) -> t.Iterator[t.Any]:
960 _log_message(self)
961 return super().__iter__() # type: ignore
963 def __bool__(self) -> bool:
964 _log_message(self)
965 return super().__bool__() # type: ignore
967 return LoggingUndefined
970class ChainableUndefined(Undefined):
971 """An undefined that is chainable, where both ``__getattr__`` and
972 ``__getitem__`` return itself rather than raising an
973 :exc:`UndefinedError`.
975 >>> foo = ChainableUndefined(name='foo')
976 >>> str(foo.bar['baz'])
977 ''
978 >>> foo.bar['baz'] + 42
979 Traceback (most recent call last):
980 ...
981 jinja2.exceptions.UndefinedError: 'foo' is undefined
983 .. versionadded:: 2.11.0
984 """
986 __slots__ = ()
988 def __html__(self) -> str:
989 return str(self)
991 def __getattr__(self, name: str) -> "ChainableUndefined":
992 # Raise AttributeError on requests for names that appear to be unimplemented
993 # dunder methods to avoid confusing Python with truthy non-method objects that
994 # do not implement the protocol being probed for. e.g., copy.copy(Undefined())
995 # fails spectacularly if getattr(Undefined(), '__setstate__') returns an
996 # Undefined object instead of raising AttributeError to signal that it does not
997 # support that style of object initialization.
998 if name[:2] == "__" and name[-2:] == "__":
999 raise AttributeError(name)
1001 return self
1003 def __getitem__(self, _name: str) -> "ChainableUndefined": # type: ignore[override]
1004 return self
1007class DebugUndefined(Undefined):
1008 """An undefined that returns the debug info when printed.
1010 >>> foo = DebugUndefined(name='foo')
1011 >>> str(foo)
1012 '{{ foo }}'
1013 >>> not foo
1014 True
1015 >>> foo + 42
1016 Traceback (most recent call last):
1017 ...
1018 jinja2.exceptions.UndefinedError: 'foo' is undefined
1019 """
1021 __slots__ = ()
1023 def __str__(self) -> str:
1024 if self._undefined_hint:
1025 message = f"undefined value printed: {self._undefined_hint}"
1027 elif self._undefined_obj is missing:
1028 message = self._undefined_name # type: ignore
1030 else:
1031 message = (
1032 f"no such element: {object_type_repr(self._undefined_obj)}"
1033 f"[{self._undefined_name!r}]"
1034 )
1036 return f"{{{{ {message} }}}}"
1039class StrictUndefined(Undefined):
1040 """An undefined that barks on print and iteration as well as boolean
1041 tests and all kinds of comparisons. In other words: you can do nothing
1042 with it except checking if it's defined using the `defined` test.
1044 >>> foo = StrictUndefined(name='foo')
1045 >>> str(foo)
1046 Traceback (most recent call last):
1047 ...
1048 jinja2.exceptions.UndefinedError: 'foo' is undefined
1049 >>> not foo
1050 Traceback (most recent call last):
1051 ...
1052 jinja2.exceptions.UndefinedError: 'foo' is undefined
1053 >>> foo + 42
1054 Traceback (most recent call last):
1055 ...
1056 jinja2.exceptions.UndefinedError: 'foo' is undefined
1057 """
1059 __slots__ = ()
1060 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
1061 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
1062 __contains__ = Undefined._fail_with_undefined_error