Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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 = concat(
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 = concat(self._stack[self._depth](self._context))
386 if self._context.eval_ctx.autoescape:
387 return Markup(rv)
389 return rv
392class LoopContext:
393 """A wrapper iterable for dynamic ``for`` loops, with information
394 about the loop and iteration.
395 """
397 #: Current iteration of the loop, starting at 0.
398 index0 = -1
400 _length: t.Optional[int] = None
401 _after: t.Any = missing
402 _current: t.Any = missing
403 _before: t.Any = missing
404 _last_changed_value: t.Any = missing
406 def __init__(
407 self,
408 iterable: t.Iterable[V],
409 undefined: t.Type["Undefined"],
410 recurse: t.Optional["LoopRenderFunc"] = None,
411 depth0: int = 0,
412 ) -> None:
413 """
414 :param iterable: Iterable to wrap.
415 :param undefined: :class:`Undefined` class to use for next and
416 previous items.
417 :param recurse: The function to render the loop body when the
418 loop is marked recursive.
419 :param depth0: Incremented when looping recursively.
420 """
421 self._iterable = iterable
422 self._iterator = self._to_iterator(iterable)
423 self._undefined = undefined
424 self._recurse = recurse
425 #: How many levels deep a recursive loop currently is, starting at 0.
426 self.depth0 = depth0
428 @staticmethod
429 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
430 return iter(iterable)
432 @property
433 def length(self) -> int:
434 """Length of the iterable.
436 If the iterable is a generator or otherwise does not have a
437 size, it is eagerly evaluated to get a size.
438 """
439 if self._length is not None:
440 return self._length
442 try:
443 self._length = len(self._iterable) # type: ignore
444 except TypeError:
445 iterable = list(self._iterator)
446 self._iterator = self._to_iterator(iterable)
447 self._length = len(iterable) + self.index + (self._after is not missing)
449 return self._length
451 def __len__(self) -> int:
452 return self.length
454 @property
455 def depth(self) -> int:
456 """How many levels deep a recursive loop currently is, starting at 1."""
457 return self.depth0 + 1
459 @property
460 def index(self) -> int:
461 """Current iteration of the loop, starting at 1."""
462 return self.index0 + 1
464 @property
465 def revindex0(self) -> int:
466 """Number of iterations from the end of the loop, ending at 0.
468 Requires calculating :attr:`length`.
469 """
470 return self.length - self.index
472 @property
473 def revindex(self) -> int:
474 """Number of iterations from the end of the loop, ending at 1.
476 Requires calculating :attr:`length`.
477 """
478 return self.length - self.index0
480 @property
481 def first(self) -> bool:
482 """Whether this is the first iteration of the loop."""
483 return self.index0 == 0
485 def _peek_next(self) -> t.Any:
486 """Return the next element in the iterable, or :data:`missing`
487 if the iterable is exhausted. Only peeks one item ahead, caching
488 the result in :attr:`_last` for use in subsequent checks. The
489 cache is reset when :meth:`__next__` is called.
490 """
491 if self._after is not missing:
492 return self._after
494 self._after = next(self._iterator, missing)
495 return self._after
497 @property
498 def last(self) -> bool:
499 """Whether this is the last iteration of the loop.
501 Causes the iterable to advance early. See
502 :func:`itertools.groupby` for issues this can cause.
503 The :func:`groupby` filter avoids that issue.
504 """
505 return self._peek_next() is missing
507 @property
508 def previtem(self) -> t.Union[t.Any, "Undefined"]:
509 """The item in the previous iteration. Undefined during the
510 first iteration.
511 """
512 if self.first:
513 return self._undefined("there is no previous item")
515 return self._before
517 @property
518 def nextitem(self) -> t.Union[t.Any, "Undefined"]:
519 """The item in the next iteration. Undefined during the last
520 iteration.
522 Causes the iterable to advance early. See
523 :func:`itertools.groupby` for issues this can cause.
524 The :func:`jinja-filters.groupby` filter avoids that issue.
525 """
526 rv = self._peek_next()
528 if rv is missing:
529 return self._undefined("there is no next item")
531 return rv
533 def cycle(self, *args: V) -> V:
534 """Return a value from the given args, cycling through based on
535 the current :attr:`index0`.
537 :param args: One or more values to cycle through.
538 """
539 if not args:
540 raise TypeError("no items for cycling given")
542 return args[self.index0 % len(args)]
544 def changed(self, *value: t.Any) -> bool:
545 """Return ``True`` if previously called with a different value
546 (including when called for the first time).
548 :param value: One or more values to compare to the last call.
549 """
550 if self._last_changed_value != value:
551 self._last_changed_value = value
552 return True
554 return False
556 def __iter__(self) -> "LoopContext":
557 return self
559 def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:
560 if self._after is not missing:
561 rv = self._after
562 self._after = missing
563 else:
564 rv = next(self._iterator)
566 self.index0 += 1
567 self._before = self._current
568 self._current = rv
569 return rv, self
571 @internalcode
572 def __call__(self, iterable: t.Iterable[V]) -> str:
573 """When iterating over nested data, render the body of the loop
574 recursively with the given inner iterable data.
576 The loop must have the ``recursive`` marker for this to work.
577 """
578 if self._recurse is None:
579 raise TypeError(
580 "The loop must have the 'recursive' marker to be called recursively."
581 )
583 return self._recurse(iterable, self._recurse, depth=self.depth)
585 def __repr__(self) -> str:
586 return f"<{type(self).__name__} {self.index}/{self.length}>"
589class AsyncLoopContext(LoopContext):
590 _iterator: t.AsyncIterator[t.Any] # type: ignore
592 @staticmethod
593 def _to_iterator( # type: ignore
594 iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]],
595 ) -> t.AsyncIterator[V]:
596 return auto_aiter(iterable)
598 @property
599 async def length(self) -> int: # type: ignore
600 if self._length is not None:
601 return self._length
603 try:
604 self._length = len(self._iterable) # type: ignore
605 except TypeError:
606 iterable = [x async for x in self._iterator]
607 self._iterator = self._to_iterator(iterable)
608 self._length = len(iterable) + self.index + (self._after is not missing)
610 return self._length
612 @property
613 async def revindex0(self) -> int: # type: ignore
614 return await self.length - self.index
616 @property
617 async def revindex(self) -> int: # type: ignore
618 return await self.length - self.index0
620 async def _peek_next(self) -> t.Any:
621 if self._after is not missing:
622 return self._after
624 try:
625 self._after = await self._iterator.__anext__()
626 except StopAsyncIteration:
627 self._after = missing
629 return self._after
631 @property
632 async def last(self) -> bool: # type: ignore
633 return await self._peek_next() is missing
635 @property
636 async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
637 rv = await self._peek_next()
639 if rv is missing:
640 return self._undefined("there is no next item")
642 return rv
644 def __aiter__(self) -> "AsyncLoopContext":
645 return self
647 async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:
648 if self._after is not missing:
649 rv = self._after
650 self._after = missing
651 else:
652 rv = await self._iterator.__anext__()
654 self.index0 += 1
655 self._before = self._current
656 self._current = rv
657 return rv, self
660class Macro:
661 """Wraps a macro function."""
663 def __init__(
664 self,
665 environment: "Environment",
666 func: t.Callable[..., str],
667 name: str,
668 arguments: t.List[str],
669 catch_kwargs: bool,
670 catch_varargs: bool,
671 caller: bool,
672 default_autoescape: t.Optional[bool] = None,
673 ):
674 self._environment = environment
675 self._func = func
676 self._argument_count = len(arguments)
677 self.name = name
678 self.arguments = arguments
679 self.catch_kwargs = catch_kwargs
680 self.catch_varargs = catch_varargs
681 self.caller = caller
682 self.explicit_caller = "caller" in arguments
684 if default_autoescape is None:
685 if callable(environment.autoescape):
686 default_autoescape = environment.autoescape(None)
687 else:
688 default_autoescape = environment.autoescape
690 self._default_autoescape = default_autoescape
692 @internalcode
693 @pass_eval_context
694 def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:
695 # This requires a bit of explanation, In the past we used to
696 # decide largely based on compile-time information if a macro is
697 # safe or unsafe. While there was a volatile mode it was largely
698 # unused for deciding on escaping. This turns out to be
699 # problematic for macros because whether a macro is safe depends not
700 # on the escape mode when it was defined, but rather when it was used.
701 #
702 # Because however we export macros from the module system and
703 # there are historic callers that do not pass an eval context (and
704 # will continue to not pass one), we need to perform an instance
705 # check here.
706 #
707 # This is considered safe because an eval context is not a valid
708 # argument to callables otherwise anyway. Worst case here is
709 # that if no eval context is passed we fall back to the compile
710 # time autoescape flag.
711 if args and isinstance(args[0], EvalContext):
712 autoescape = args[0].autoescape
713 args = args[1:]
714 else:
715 autoescape = self._default_autoescape
717 # try to consume the positional arguments
718 arguments = list(args[: self._argument_count])
719 off = len(arguments)
721 # For information why this is necessary refer to the handling
722 # of caller in the `macro_body` handler in the compiler.
723 found_caller = False
725 # if the number of arguments consumed is not the number of
726 # arguments expected we start filling in keyword arguments
727 # and defaults.
728 if off != self._argument_count:
729 for name in self.arguments[len(arguments) :]:
730 try:
731 value = kwargs.pop(name)
732 except KeyError:
733 value = missing
734 if name == "caller":
735 found_caller = True
736 arguments.append(value)
737 else:
738 found_caller = self.explicit_caller
740 # it's important that the order of these arguments does not change
741 # if not also changed in the compiler's `function_scoping` method.
742 # the order is caller, keyword arguments, positional arguments!
743 if self.caller and not found_caller:
744 caller = kwargs.pop("caller", None)
745 if caller is None:
746 caller = self._environment.undefined("No caller defined", name="caller")
747 arguments.append(caller)
749 if self.catch_kwargs:
750 arguments.append(kwargs)
751 elif kwargs:
752 if "caller" in kwargs:
753 raise TypeError(
754 f"macro {self.name!r} was invoked with two values for the special"
755 " caller argument. This is most likely a bug."
756 )
757 raise TypeError(
758 f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}"
759 )
760 if self.catch_varargs:
761 arguments.append(args[self._argument_count :])
762 elif len(args) > self._argument_count:
763 raise TypeError(
764 f"macro {self.name!r} takes not more than"
765 f" {len(self.arguments)} argument(s)"
766 )
768 return self._invoke(arguments, autoescape)
770 async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
771 rv = await self._func(*arguments) # type: ignore
773 if autoescape:
774 return Markup(rv)
776 return rv # type: ignore
778 def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
779 if self._environment.is_async:
780 return self._async_invoke(arguments, autoescape) # type: ignore
782 rv = self._func(*arguments)
784 if autoescape:
785 rv = Markup(rv)
787 return rv
789 def __repr__(self) -> str:
790 name = "anonymous" if self.name is None else repr(self.name)
791 return f"<{type(self).__name__} {name}>"
794class Undefined:
795 """The default undefined type. This undefined type can be printed and
796 iterated over, but every other access will raise an :exc:`UndefinedError`:
798 >>> foo = Undefined(name='foo')
799 >>> str(foo)
800 ''
801 >>> not foo
802 True
803 >>> foo + 42
804 Traceback (most recent call last):
805 ...
806 jinja2.exceptions.UndefinedError: 'foo' is undefined
807 """
809 __slots__ = (
810 "_undefined_hint",
811 "_undefined_obj",
812 "_undefined_name",
813 "_undefined_exception",
814 )
816 def __init__(
817 self,
818 hint: t.Optional[str] = None,
819 obj: t.Any = missing,
820 name: t.Optional[str] = None,
821 exc: t.Type[TemplateRuntimeError] = UndefinedError,
822 ) -> None:
823 self._undefined_hint = hint
824 self._undefined_obj = obj
825 self._undefined_name = name
826 self._undefined_exception = exc
828 @property
829 def _undefined_message(self) -> str:
830 """Build a message about the undefined value based on how it was
831 accessed.
832 """
833 if self._undefined_hint:
834 return self._undefined_hint
836 if self._undefined_obj is missing:
837 return f"{self._undefined_name!r} is undefined"
839 if not isinstance(self._undefined_name, str):
840 return (
841 f"{object_type_repr(self._undefined_obj)} has no"
842 f" element {self._undefined_name!r}"
843 )
845 return (
846 f"{object_type_repr(self._undefined_obj)!r} has no"
847 f" attribute {self._undefined_name!r}"
848 )
850 @internalcode
851 def _fail_with_undefined_error(
852 self, *args: t.Any, **kwargs: t.Any
853 ) -> "te.NoReturn":
854 """Raise an :exc:`UndefinedError` when operations are performed
855 on the undefined value.
856 """
857 raise self._undefined_exception(self._undefined_message)
859 @internalcode
860 def __getattr__(self, name: str) -> t.Any:
861 if name[:2] == "__":
862 raise AttributeError(name)
864 return self._fail_with_undefined_error()
866 __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error
867 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error
868 __truediv__ = __rtruediv__ = _fail_with_undefined_error
869 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error
870 __mod__ = __rmod__ = _fail_with_undefined_error
871 __pos__ = __neg__ = _fail_with_undefined_error
872 __call__ = __getitem__ = _fail_with_undefined_error
873 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error
874 __int__ = __float__ = __complex__ = _fail_with_undefined_error
875 __pow__ = __rpow__ = _fail_with_undefined_error
877 def __eq__(self, other: t.Any) -> bool:
878 return type(self) is type(other)
880 def __ne__(self, other: t.Any) -> bool:
881 return not self.__eq__(other)
883 def __hash__(self) -> int:
884 return id(type(self))
886 def __str__(self) -> str:
887 return ""
889 def __len__(self) -> int:
890 return 0
892 def __iter__(self) -> t.Iterator[t.Any]:
893 yield from ()
895 async def __aiter__(self) -> t.AsyncIterator[t.Any]:
896 for _ in ():
897 yield
899 def __bool__(self) -> bool:
900 return False
902 def __repr__(self) -> str:
903 return "Undefined"
906def make_logging_undefined(
907 logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined
908) -> t.Type[Undefined]:
909 """Given a logger object this returns a new undefined class that will
910 log certain failures. It will log iterations and printing. If no
911 logger is given a default logger is created.
913 Example::
915 logger = logging.getLogger(__name__)
916 LoggingUndefined = make_logging_undefined(
917 logger=logger,
918 base=Undefined
919 )
921 .. versionadded:: 2.8
923 :param logger: the logger to use. If not provided, a default logger
924 is created.
925 :param base: the base class to add logging functionality to. This
926 defaults to :class:`Undefined`.
927 """
928 if logger is None:
929 import logging
931 logger = logging.getLogger(__name__)
932 logger.addHandler(logging.StreamHandler(sys.stderr))
934 def _log_message(undef: Undefined) -> None:
935 logger.warning("Template variable warning: %s", undef._undefined_message)
937 class LoggingUndefined(base): # type: ignore
938 __slots__ = ()
940 def _fail_with_undefined_error( # type: ignore
941 self, *args: t.Any, **kwargs: t.Any
942 ) -> "te.NoReturn":
943 try:
944 super()._fail_with_undefined_error(*args, **kwargs)
945 except self._undefined_exception as e:
946 logger.error("Template variable error: %s", e) # type: ignore
947 raise e
949 def __str__(self) -> str:
950 _log_message(self)
951 return super().__str__() # type: ignore
953 def __iter__(self) -> t.Iterator[t.Any]:
954 _log_message(self)
955 return super().__iter__() # type: ignore
957 def __bool__(self) -> bool:
958 _log_message(self)
959 return super().__bool__() # type: ignore
961 return LoggingUndefined
964class ChainableUndefined(Undefined):
965 """An undefined that is chainable, where both ``__getattr__`` and
966 ``__getitem__`` return itself rather than raising an
967 :exc:`UndefinedError`.
969 >>> foo = ChainableUndefined(name='foo')
970 >>> str(foo.bar['baz'])
971 ''
972 >>> foo.bar['baz'] + 42
973 Traceback (most recent call last):
974 ...
975 jinja2.exceptions.UndefinedError: 'foo' is undefined
977 .. versionadded:: 2.11.0
978 """
980 __slots__ = ()
982 def __html__(self) -> str:
983 return str(self)
985 def __getattr__(self, _: str) -> "ChainableUndefined":
986 return self
988 __getitem__ = __getattr__ # type: ignore
991class DebugUndefined(Undefined):
992 """An undefined that returns the debug info when printed.
994 >>> foo = DebugUndefined(name='foo')
995 >>> str(foo)
996 '{{ foo }}'
997 >>> not foo
998 True
999 >>> foo + 42
1000 Traceback (most recent call last):
1001 ...
1002 jinja2.exceptions.UndefinedError: 'foo' is undefined
1003 """
1005 __slots__ = ()
1007 def __str__(self) -> str:
1008 if self._undefined_hint:
1009 message = f"undefined value printed: {self._undefined_hint}"
1011 elif self._undefined_obj is missing:
1012 message = self._undefined_name # type: ignore
1014 else:
1015 message = (
1016 f"no such element: {object_type_repr(self._undefined_obj)}"
1017 f"[{self._undefined_name!r}]"
1018 )
1020 return f"{{{{ {message} }}}}"
1023class StrictUndefined(Undefined):
1024 """An undefined that barks on print and iteration as well as boolean
1025 tests and all kinds of comparisons. In other words: you can do nothing
1026 with it except checking if it's defined using the `defined` test.
1028 >>> foo = StrictUndefined(name='foo')
1029 >>> str(foo)
1030 Traceback (most recent call last):
1031 ...
1032 jinja2.exceptions.UndefinedError: 'foo' is undefined
1033 >>> not foo
1034 Traceback (most recent call last):
1035 ...
1036 jinja2.exceptions.UndefinedError: 'foo' is undefined
1037 >>> foo + 42
1038 Traceback (most recent call last):
1039 ...
1040 jinja2.exceptions.UndefinedError: 'foo' is undefined
1041 """
1043 __slots__ = ()
1044 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error
1045 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error
1046 __contains__ = Undefined._fail_with_undefined_error
1049# Remove slots attributes, after the metaclass is applied they are
1050# unneeded and contain wrong data for subclasses.
1051del (
1052 Undefined.__slots__,
1053 ChainableUndefined.__slots__,
1054 DebugUndefined.__slots__,
1055 StrictUndefined.__slots__,
1056)