codekingpro/portable-devtools
115k
1"""The runtime functions and state used by compiled templates."""2 3import functools4import sys5import typing as t6from collections import abc7from itertools import chain8 9from markupsafe import escape # noqa: F40110from markupsafe import Markup11from markupsafe import soft_str12 13from .async_utils import auto_aiter14from .async_utils import auto_await # noqa: F40115from .exceptions import TemplateNotFound # noqa: F40116from .exceptions import TemplateRuntimeError # noqa: F40117from .exceptions import UndefinedError18from .nodes import EvalContext19from .utils import _PassArg20from .utils import concat21from .utils import internalcode22from .utils import missing23from .utils import Namespace # noqa: F40124from .utils import object_type_repr25from .utils import pass_eval_context26 27V = t.TypeVar("V")28F = t.TypeVar("F", bound=t.Callable[..., t.Any])29 30if t.TYPE_CHECKING:31 import logging32 33 import typing_extensions as te34 35 from .environment import Environment36 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 runtime47exported = [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 the72 environment.73 """74 return x75 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 = vars107 else:108 parent = dict(globals or (), **vars)109 if locals:110 # if the parent is shared a copy should be created because111 # we don't want to modify the dict passed112 if shared:113 parent = dict(parent)114 for key, value in locals.items():115 if value is not missing:116 parent[key] = value117 return environment.context_class(118 environment, parent, template_name, blocks, globals=globals119 )120 121 122class TemplateReference:123 """The `self` in templates."""124 125 def __init__(self, context: "Context") -> None:126 self.__context = context127 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.register145class Context:146 """The template context holds the variables of a template. It stores the147 values passed to the template and also the names the template exports.148 Creating instances is neither supported nor useful as it's created149 automatically at various stages of the template evaluation and should not150 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 generated154 template code only. Template filters and global functions marked as155 :func:`pass_context` get the active context passed as first argument156 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 an162 :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 = parent174 self.vars: t.Dict[str, t.Any] = {}175 self.environment: Environment = environment176 self.eval_ctx = EvalContext(self.environment, name)177 self.exported_vars: t.Set[str] = set()178 self.name = name179 self.globals_keys = set() if globals is None else set(globals)180 181 # create the initial mapping of blocks. Whenever template inheritance182 # takes place the runtime will update this mapping with the new blocks183 # 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) + 1193 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 is202 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 default211 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, override217 :meth:`resolve_or_missing`, not this method. The various lookup218 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 rv228 229 def resolve_or_missing(self, key: str) -> t.Any:230 """Look up a variable by name, or return a ``missing`` sentinel231 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 this235 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 missing246 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 exported253 variables. For optimizations reasons this might not return an254 actual copy so be careful with using it.255 """256 if not self.vars:257 return self.parent258 if not self.parent:259 return self.vars260 return dict(self.parent, **self.vars)261 262 @internalcode263 def call(264 __self,265 __obj: t.Callable[..., t.Any],266 *args: t.Any,267 **kwargs: t.Any, # noqa: B902268 ) -> t.Union[t.Any, "Undefined"]:269 """Call the callable with the arguments and keyword arguments270 provided but inject the active context or environment as first271 argument if the callable has :func:`pass_context` or272 :func:`pass_environment`.273 """274 if __debug__:275 __traceback_hide__ = True # noqa276 277 # Allow callable classes to take a context278 if (279 hasattr(__obj, "__call__") # noqa: B004280 and _PassArg.from_obj(__obj.__call__) is not None281 ):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 in288 # loops and blocks without mutating the context itself289 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,) + args294 elif pass_arg is _PassArg.eval_context:295 args = (__self.eval_ctx,) + args296 elif pass_arg is _PassArg.environment:297 args = (__self.environment,) + args298 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 is312 used in situations where the system needs a new context in the same313 template that is independent.314 """315 context = new_context(316 self.environment, self.name, {}, self.get_all(), True, None, locals317 )318 context.eval_ctx = self.eval_ctx319 context.blocks.update((k, list(v)) for k, v in self.blocks.items())320 return context321 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.parent328 329 def __getitem__(self, key: str) -> t.Any:330 """Look up a variable by name with ``[]`` syntax, or raise a331 ``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 item339 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 = name355 self._context = context356 self._stack = stack357 self._depth = depth358 359 @property360 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 @internalcode369 async def _async_call(self) -> str:370 rv = self._context.environment.concat( # type: ignore371 [x async for x in self._stack[self._depth](self._context)] # type: ignore372 )373 374 if self._context.eval_ctx.autoescape:375 return Markup(rv)376 377 return rv378 379 @internalcode380 def __call__(self) -> str:381 if self._context.environment.is_async:382 return self._async_call() # type: ignore383 384 rv = self._context.environment.concat( # type: ignore385 self._stack[self._depth](self._context)386 )387 388 if self._context.eval_ctx.autoescape:389 return Markup(rv)390 391 return rv392 393 394class LoopContext:395 """A wrapper iterable for dynamic ``for`` loops, with information396 about the loop and iteration.397 """398 399 #: Current iteration of the loop, starting at 0.400 index0 = -1401 402 _length: t.Optional[int] = None403 _after: t.Any = missing404 _current: t.Any = missing405 _before: t.Any = missing406 _last_changed_value: t.Any = missing407 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 and418 previous items.419 :param recurse: The function to render the loop body when the420 loop is marked recursive.421 :param depth0: Incremented when looping recursively.422 """423 self._iterable = iterable424 self._iterator = self._to_iterator(iterable)425 self._undefined = undefined426 self._recurse = recurse427 #: How many levels deep a recursive loop currently is, starting at 0.428 self.depth0 = depth0429 430 @staticmethod431 def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]:432 return iter(iterable)433 434 @property435 def length(self) -> int:436 """Length of the iterable.437 438 If the iterable is a generator or otherwise does not have a439 size, it is eagerly evaluated to get a size.440 """441 if self._length is not None:442 return self._length443 444 try:445 self._length = len(self._iterable) # type: ignore446 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._length452 453 def __len__(self) -> int:454 return self.length455 456 @property457 def depth(self) -> int:458 """How many levels deep a recursive loop currently is, starting at 1."""459 return self.depth0 + 1460 461 @property462 def index(self) -> int:463 """Current iteration of the loop, starting at 1."""464 return self.index0 + 1465 466 @property467 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.index473 474 @property475 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.index0481 482 @property483 def first(self) -> bool:484 """Whether this is the first iteration of the loop."""485 return self.index0 == 0486 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, caching490 the result in :attr:`_last` for use in subsequent checks. The491 cache is reset when :meth:`__next__` is called.492 """493 if self._after is not missing:494 return self._after495 496 self._after = next(self._iterator, missing)497 return self._after498 499 @property500 def last(self) -> bool:501 """Whether this is the last iteration of the loop.502 503 Causes the iterable to advance early. See504 :func:`itertools.groupby` for issues this can cause.505 The :func:`groupby` filter avoids that issue.506 """507 return self._peek_next() is missing508 509 @property510 def previtem(self) -> t.Union[t.Any, "Undefined"]:511 """The item in the previous iteration. Undefined during the512 first iteration.513 """514 if self.first:515 return self._undefined("there is no previous item")516 517 return self._before518 519 @property520 def nextitem(self) -> t.Union[t.Any, "Undefined"]:521 """The item in the next iteration. Undefined during the last522 iteration.523 524 Causes the iterable to advance early. See525 :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 rv534 535 def cycle(self, *args: V) -> V:536 """Return a value from the given args, cycling through based on537 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 value548 (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 = value554 return True555 556 return False557 558 def __iter__(self) -> "LoopContext":559 return self560 561 def __next__(self) -> t.Tuple[t.Any, "LoopContext"]:562 if self._after is not missing:563 rv = self._after564 self._after = missing565 else:566 rv = next(self._iterator)567 568 self.index0 += 1569 self._before = self._current570 self._current = rv571 return rv, self572 573 @internalcode574 def __call__(self, iterable: t.Iterable[V]) -> str:575 """When iterating over nested data, render the body of the loop576 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: ignore593 594 @staticmethod595 def _to_iterator( # type: ignore596 iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]],597 ) -> t.AsyncIterator[V]:598 return auto_aiter(iterable)599 600 @property601 async def length(self) -> int: # type: ignore602 if self._length is not None:603 return self._length604 605 try:606 self._length = len(self._iterable) # type: ignore607 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._length613 614 @property615 async def revindex0(self) -> int: # type: ignore616 return await self.length - self.index617 618 @property619 async def revindex(self) -> int: # type: ignore620 return await self.length - self.index0621 622 async def _peek_next(self) -> t.Any:623 if self._after is not missing:624 return self._after625 626 try:627 self._after = await self._iterator.__anext__()628 except StopAsyncIteration:629 self._after = missing630 631 return self._after632 633 @property634 async def last(self) -> bool: # type: ignore635 return await self._peek_next() is missing636 637 @property638 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 rv645 646 def __aiter__(self) -> "AsyncLoopContext":647 return self648 649 async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]:650 if self._after is not missing:651 rv = self._after652 self._after = missing653 else:654 rv = await self._iterator.__anext__()655 656 self.index0 += 1657 self._before = self._current658 self._current = rv659 return rv, self660 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 = environment677 self._func = func678 self._argument_count = len(arguments)679 self.name = name680 self.arguments = arguments681 self.catch_kwargs = catch_kwargs682 self.catch_varargs = catch_varargs683 self.caller = caller684 self.explicit_caller = "caller" in arguments685 686 if default_autoescape is None:687 if callable(environment.autoescape):688 default_autoescape = environment.autoescape(None)689 else:690 default_autoescape = environment.autoescape691 692 self._default_autoescape = default_autoescape693 694 @internalcode695 @pass_eval_context696 def __call__(self, *args: t.Any, **kwargs: t.Any) -> str:697 # This requires a bit of explanation, In the past we used to698 # decide largely based on compile-time information if a macro is699 # safe or unsafe. While there was a volatile mode it was largely700 # unused for deciding on escaping. This turns out to be701 # problematic for macros because whether a macro is safe depends not702 # 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 and705 # there are historic callers that do not pass an eval context (and706 # will continue to not pass one), we need to perform an instance707 # check here.708 #709 # This is considered safe because an eval context is not a valid710 # argument to callables otherwise anyway. Worst case here is711 # that if no eval context is passed we fall back to the compile712 # time autoescape flag.713 if args and isinstance(args[0], EvalContext):714 autoescape = args[0].autoescape715 args = args[1:]716 else:717 autoescape = self._default_autoescape718 719 # try to consume the positional arguments720 arguments = list(args[: self._argument_count])721 off = len(arguments)722 723 # For information why this is necessary refer to the handling724 # of caller in the `macro_body` handler in the compiler.725 found_caller = False726 727 # if the number of arguments consumed is not the number of728 # arguments expected we start filling in keyword arguments729 # 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 = missing736 if name == "caller":737 found_caller = True738 arguments.append(value)739 else:740 found_caller = self.explicit_caller741 742 # it's important that the order of these arguments does not change743 # 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: ignore774 775 if autoescape:776 return Markup(rv)777 778 return rv # type: ignore779 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: ignore783 784 rv = self._func(*arguments)785 786 if autoescape:787 rv = Markup(rv)788 789 return rv790 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 as798 a boolean. Any other operation will raise an :exc:`UndefinedError`.799 800 >>> foo = Undefined(name='foo')801 >>> str(foo)802 ''803 >>> not foo804 True805 >>> foo + 42806 Traceback (most recent call last):807 ...808 jinja2.exceptions.UndefinedError: 'foo' is undefined809 """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 = hint826 self._undefined_obj = obj827 self._undefined_name = name828 self._undefined_exception = exc829 830 @property831 def _undefined_message(self) -> str:832 """Build a message about the undefined value based on how it was833 accessed.834 """835 if self._undefined_hint:836 return self._undefined_hint837 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 @internalcode853 def _fail_with_undefined_error(854 self, *args: t.Any, **kwargs: t.Any855 ) -> "te.NoReturn":856 """Raise an :exc:`UndefinedError` when operations are performed857 on the undefined value.858 """859 raise self._undefined_exception(self._undefined_message)860 861 @internalcode862 def __getattr__(self, name: str) -> t.Any:863 # Raise AttributeError on requests for names that appear to be unimplemented864 # dunder methods to keep Python's internal protocol probing behaviors working865 # properly in cases where another exception type could cause unexpected or866 # 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_error873 __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error874 __truediv__ = __rtruediv__ = _fail_with_undefined_error875 __floordiv__ = __rfloordiv__ = _fail_with_undefined_error876 __mod__ = __rmod__ = _fail_with_undefined_error877 __pos__ = __neg__ = _fail_with_undefined_error878 __call__ = __getitem__ = _fail_with_undefined_error879 __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error880 __int__ = __float__ = __complex__ = _fail_with_undefined_error881 __pow__ = __rpow__ = _fail_with_undefined_error882 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 0897 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 yield904 905 def __bool__(self) -> bool:906 return False907 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] = Undefined914) -> t.Type[Undefined]:915 """Given a logger object this returns a new undefined class that will916 log certain failures. It will log iterations and printing. If no917 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=Undefined925 )926 927 .. versionadded:: 2.8928 929 :param logger: the logger to use. If not provided, a default logger930 is created.931 :param base: the base class to add logging functionality to. This932 defaults to :class:`Undefined`.933 """934 if logger is None:935 import logging936 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: ignore944 __slots__ = ()945 946 def _fail_with_undefined_error( # type: ignore947 self, *args: t.Any, **kwargs: t.Any948 ) -> "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: ignore953 raise e954 955 def __str__(self) -> str:956 _log_message(self)957 return super().__str__() # type: ignore958 959 def __iter__(self) -> t.Iterator[t.Any]:960 _log_message(self)961 return super().__iter__() # type: ignore962 963 def __bool__(self) -> bool:964 _log_message(self)965 return super().__bool__() # type: ignore966 967 return LoggingUndefined968 969 970class ChainableUndefined(Undefined):971 """An undefined that is chainable, where both ``__getattr__`` and972 ``__getitem__`` return itself rather than raising an973 :exc:`UndefinedError`.974 975 >>> foo = ChainableUndefined(name='foo')976 >>> str(foo.bar['baz'])977 ''978 >>> foo.bar['baz'] + 42979 Traceback (most recent call last):980 ...981 jinja2.exceptions.UndefinedError: 'foo' is undefined982 983 .. versionadded:: 2.11.0984 """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 unimplemented993 # dunder methods to avoid confusing Python with truthy non-method objects that994 # do not implement the protocol being probed for. e.g., copy.copy(Undefined())995 # fails spectacularly if getattr(Undefined(), '__setstate__') returns an996 # Undefined object instead of raising AttributeError to signal that it does not997 # support that style of object initialization.998 if name[:2] == "__" and name[-2:] == "__":999 raise AttributeError(name)1000 1001 return self1002 1003 def __getitem__(self, _name: str) -> "ChainableUndefined": # type: ignore[override]1004 return self1005 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 foo1014 True1015 >>> foo + 421016 Traceback (most recent call last):1017 ...1018 jinja2.exceptions.UndefinedError: 'foo' is undefined1019 """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: ignore1029 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 boolean1041 tests and all kinds of comparisons. In other words: you can do nothing1042 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 undefined1049 >>> not foo1050 Traceback (most recent call last):1051 ...1052 jinja2.exceptions.UndefinedError: 'foo' is undefined1053 >>> foo + 421054 Traceback (most recent call last):1055 ...1056 jinja2.exceptions.UndefinedError: 'foo' is undefined1057 """1058 1059 __slots__ = ()1060 __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error1061 __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error1062 __contains__ = Undefined._fail_with_undefined_error1063 