1"""The runtime functions and state used by compiled templates."""
2
3import functools
4import sys
5import typing as t
6from collections import abc
7from itertools import chain
8
9from markupsafe import escape # noqa: F401
10from markupsafe import Markup
11from markupsafe import soft_str
12
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
26
27V = t.TypeVar("V")
28F = t.TypeVar("F", bound=t.Callable[..., t.Any])
29
30if t.TYPE_CHECKING:
31 import logging
32
33 import typing_extensions as te
34
35 from .environment import Environment
36
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: ...
44
45
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]
68
69
70def identity(x: V) -> V:
71 """Returns its argument. Useful for certain things in the
72 environment.
73 """
74 return x
75
76
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)
86
87
88def str_join(seq: t.Iterable[t.Any]) -> str:
89 """Simple args to string conversion and concatenation."""
90 return concat(map(str, seq))
91
92
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 )
120
121
122class TemplateReference:
123 """The `self` in templates."""
124
125 def __init__(self, context: "Context") -> None:
126 self.__context = context
127
128 def __getitem__(self, name: str) -> t.Any:
129 blocks = self.__context.blocks[name]
130 return BlockReference(name, self.__context, blocks, 0)
131
132 def __repr__(self) -> str:
133 return f"<{type(self).__name__} {self.__context.name!r}>"
134
135
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())
140
141 return t.cast(F, f_all)
142
143
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.
151
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.
157
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 """
164
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)
180
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()}
185
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)
199
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.
203
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
211
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.
215
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.
219
220 :param key: The variable name to look up.
221 """
222 rv = self.resolve_or_missing(key)
223
224 if rv is missing:
225 return self.environment.undefined(name=key)
226
227 return rv
228
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.
232
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.
236
237 :param key: The variable name to look up.
238 """
239 if key in self.vars:
240 return self.vars[key]
241
242 if key in self.parent:
243 return self.parent[key]
244
245 return missing
246
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}
250
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)
261
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
276
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__
283
284 pass_arg = _PassArg.from_obj(__obj)
285
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
298
299 kwargs.pop("_block_vars", None)
300 kwargs.pop("_loop_vars", None)
301
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 )
309
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
321
322 keys = _dict_method_all(dict.keys)
323 values = _dict_method_all(dict.values)
324 items = _dict_method_all(dict.items)
325
326 def __contains__(self, name: str) -> bool:
327 return name in self.vars or name in self.parent
328
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)
334
335 if item is missing:
336 raise KeyError(key)
337
338 return item
339
340 def __repr__(self) -> str:
341 return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>"
342
343
344class BlockReference:
345 """One block on a template reference."""
346
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
358
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)
367
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 )
373
374 if self._context.eval_ctx.autoescape:
375 return Markup(rv)
376
377 return rv
378
379 @internalcode
380 def __call__(self) -> str:
381 if self._context.environment.is_async:
382 return self._async_call() # type: ignore
383
384 rv = self._context.environment.concat( # type: ignore
385 self._stack[self._depth](self._context)
386 )
387
388 if self._context.eval_ctx.autoescape:
389 return Markup(rv)
390
391 return rv
392
393
394class LoopContext:
395 """A wrapper iterable for dynamic ``for`` loops, with information
396 about the loop and iteration.
397 """
398
399 #: Current iteration of the loop, starting at 0.
400 index0 = -1
401
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
407
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
429
430 @staticmethod
431 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:
432 return iter(iterable)
433
434 @property
435 def length(self) -> int:
436 """Length of the iterable.
437
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
443
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)
450
451 return self._length
452
453 def __len__(self) -> int:
454 return self.length
455
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
460
461 @property
462 def index(self) -> int:
463 """Current iteration of the loop, starting at 1."""
464 return self.index0 + 1
465
466 @property
467 def revindex0(self) -> int:
468 """Number of iterations from the end of the loop, ending at 0.
469
470 Requires calculating :attr:`length`.
471 """
472 return self.length - self.index
473
474 @property
475 def revindex(self) -> int:
476 """Number of iterations from the end of the loop, ending at 1.
477
478 Requires calculating :attr:`length`.
479 """
480 return self.length - self.index0
481
482 @property
483 def first(self) -> bool:
484 """Whether this is the first iteration of the loop."""
485 return self.index0 == 0
486
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
495
496 self._after = next(self._iterator, missing)
497 return self._after
498
499 @property
500 def last(self) -> bool:
501 """Whether this is the last iteration of the loop.
502
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
508
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")
516
517 return self._before
518
519 @property
520 def nextitem(self) -> t.Union[t.Any, "Undefined"]:
521 """The item in the next iteration. Undefined during the last
522 iteration.
523
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()
529
530 if rv is missing:
531 return self._undefined("there is no next item")
532
533 return rv
534
535 def cycle(self, *args: V) -> V:
536 """Return a value from the given args, cycling through based on
537 the current :attr:`index0`.
538
539 :param args: One or more values to cycle through.
540 """
541 if not args:
542 raise TypeError("no items for cycling given")
543
544 return args[self.index0 % len(args)]
545
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).
549
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
555
556 return False
557
558 def __iter__(self) -> "LoopContext":
559 return self
560
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)
567
568 self.index0 += 1
569 self._before = self._current
570 self._current = rv
571 return rv, self
572
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.
577
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 )
584
585 return self._recurse(iterable, self._recurse, depth=self.depth)
586
587 def __repr__(self) -> str:
588 return f"<{type(self).__name__} {self.index}/{self.length}>"
589
590
591class AsyncLoopContext(LoopContext):
592 _iterator: t.AsyncIterator[t.Any] # type: ignore
593
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)
599
600 @property
601 async def length(self) -> int: # type: ignore
602 if self._length is not None:
603 return self._length
604
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)
611
612 return self._length
613
614 @property
615 async def revindex0(self) -> int: # type: ignore
616 return await self.length - self.index
617
618 @property
619 async def revindex(self) -> int: # type: ignore
620 return await self.length - self.index0
621
622 async def _peek_next(self) -> t.Any:
623 if self._after is not missing:
624 return self._after
625
626 try:
627 self._after = await self._iterator.__anext__()
628 except StopAsyncIteration:
629 self._after = missing
630
631 return self._after
632
633 @property
634 async def last(self) -> bool: # type: ignore
635 return await self._peek_next() is missing
636
637 @property
638 async def nextitem(self) -> t.Union[t.Any, "Undefined"]:
639 rv = await self._peek_next()
640
641 if rv is missing:
642 return self._undefined("there is no next item")
643
644 return rv
645
646 def __aiter__(self) -> "AsyncLoopContext":
647 return self
648
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__()
655
656 self.index0 += 1
657 self._before = self._current
658 self._current = rv
659 return rv, self
660
661
662class Macro:
663 """Wraps a macro function."""
664
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
685
686 if default_autoescape is None:
687 if callable(environment.autoescape):
688 default_autoescape = environment.autoescape(None)
689 else:
690 default_autoescape = environment.autoescape
691
692 self._default_autoescape = default_autoescape
693
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
718
719 # try to consume the positional arguments
720 arguments = list(args[: self._argument_count])
721 off = len(arguments)
722
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
726
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
741
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)
750
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 )
769
770 return self._invoke(arguments, autoescape)
771
772 async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str:
773 rv = await self._func(*arguments) # type: ignore
774
775 if autoescape:
776 return Markup(rv)
777
778 return rv # type: ignore
779
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
783
784 rv = self._func(*arguments)
785
786 if autoescape:
787 rv = Markup(rv)
788
789 return rv
790
791 def __repr__(self) -> str:
792 name = "anonymous" if self.name is None else repr(self.name)
793 return f"<{type(self).__name__} {name}>"
794
795
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`.
799
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 """
810
811 __slots__ = (
812 "_undefined_hint",
813 "_undefined_obj",
814 "_undefined_name",
815 "_undefined_exception",
816 )
817
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
829
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
837
838 if self._undefined_obj is missing:
839 return f"{self._undefined_name!r} is undefined"
840
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 )
846
847 return (
848 f"{object_type_repr(self._undefined_obj)!r} has no"
849 f" attribute {self._undefined_name!r}"
850 )
851
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)
860
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)
869
870 return self._fail_with_undefined_error()
871
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
882
883 def __eq__(self, other: t.Any) -> bool:
884 return type(self) is type(other)
885
886 def __ne__(self, other: t.Any) -> bool:
887 return not self.__eq__(other)
888
889 def __hash__(self) -> int:
890 return id(type(self))
891
892 def __str__(self) -> str:
893 return ""
894
895 def __len__(self) -> int:
896 return 0
897
898 def __iter__(self) -> t.Iterator[t.Any]:
899 yield from ()
900
901 async def __aiter__(self) -> t.AsyncIterator[t.Any]:
902 for _ in ():
903 yield
904
905 def __bool__(self) -> bool:
906 return False
907
908 def __repr__(self) -> str:
909 return "Undefined"
910
911
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.
918
919 Example::
920
921 logger = logging.getLogger(__name__)
922 LoggingUndefined = make_logging_undefined(
923 logger=logger,
924 base=Undefined
925 )
926
927 .. versionadded:: 2.8
928
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
936
937 logger = logging.getLogger(__name__)
938 logger.addHandler(logging.StreamHandler(sys.stderr))
939
940 def _log_message(undef: Undefined) -> None:
941 logger.warning("Template variable warning: %s", undef._undefined_message)
942
943 class LoggingUndefined(base): # type: ignore
944 __slots__ = ()
945
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
954
955 def __str__(self) -> str:
956 _log_message(self)
957 return super().__str__() # type: ignore
958
959 def __iter__(self) -> t.Iterator[t.Any]:
960 _log_message(self)
961 return super().__iter__() # type: ignore
962
963 def __bool__(self) -> bool:
964 _log_message(self)
965 return super().__bool__() # type: ignore
966
967 return LoggingUndefined
968
969
970class ChainableUndefined(Undefined):
971 """An undefined that is chainable, where both ``__getattr__`` and
972 ``__getitem__`` return itself rather than raising an
973 :exc:`UndefinedError`.
974
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
982
983 .. versionadded:: 2.11.0
984 """
985
986 __slots__ = ()
987
988 def __html__(self) -> str:
989 return str(self)
990
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)
1000
1001 return self
1002
1003 def __getitem__(self, _name: str) -> "ChainableUndefined": # type: ignore[override]
1004 return self
1005
1006
1007class DebugUndefined(Undefined):
1008 """An undefined that returns the debug info when printed.
1009
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 """
1020
1021 __slots__ = ()
1022
1023 def __str__(self) -> str:
1024 if self._undefined_hint:
1025 message = f"undefined value printed: {self._undefined_hint}"
1026
1027 elif self._undefined_obj is missing:
1028 message = self._undefined_name # type: ignore
1029
1030 else:
1031 message = (
1032 f"no such element: {object_type_repr(self._undefined_obj)}"
1033 f"[{self._undefined_name!r}]"
1034 )
1035
1036 return f"{{{{ {message} }}}}"
1037
1038
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.
1043
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 """
1058
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