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