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