]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/jinja2/runtime.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / jinja2 / runtime.py
1 """The runtime functions and state used by compiled templates."""
2 import functools
3 import sys
4 import typing as t
5 from collections import abc
6 from itertools import chain
7
8 from markupsafe import escape # noqa: F401
9 from markupsafe import Markup
10 from markupsafe import soft_str
11
12 from .async_utils import auto_aiter
13 from .async_utils import auto_await # noqa: F401
14 from .exceptions import TemplateNotFound # noqa: F401
15 from .exceptions import TemplateRuntimeError # noqa: F401
16 from .exceptions import UndefinedError
17 from .nodes import EvalContext
18 from .utils import _PassArg
19 from .utils import concat
20 from .utils import internalcode
21 from .utils import missing
22 from .utils import Namespace # noqa: F401
23 from .utils import object_type_repr
24 from .utils import pass_eval_context
25
26 V = t.TypeVar("V")
27 F = t.TypeVar("F", bound=t.Callable[..., t.Any])
28
29 if 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
45 exported = [
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 ]
61 async_exported = [
62 "AsyncLoopContext",
63 "auto_aiter",
64 "auto_await",
65 ]
66
67
68 def identity(x: V) -> V:
69 """Returns its argument. Useful for certain things in the
70 environment.
71 """
72 return x
73
74
75 def 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
86 def str_join(seq: t.Iterable[t.Any]) -> str:
87 """Simple args to string conversion and concatenation."""
88 return concat(map(str, seq))
89
90
91 def 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
120 class 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
134 def _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
143 class 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
339 class 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
387 class 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
584 class 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
655 class 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
789 class 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
901 def 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
961 class 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
988 class 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
1020 class 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.
1048 del (
1049 Undefined.__slots__,
1050 ChainableUndefined.__slots__,
1051 DebugUndefined.__slots__,
1052 StrictUndefined.__slots__,
1053 )