diff options
Diffstat (limited to 'venv/lib/python3.11/site-packages/jinja2/runtime.py')
-rw-r--r-- | venv/lib/python3.11/site-packages/jinja2/runtime.py | 1051 |
1 files changed, 0 insertions, 1051 deletions
diff --git a/venv/lib/python3.11/site-packages/jinja2/runtime.py b/venv/lib/python3.11/site-packages/jinja2/runtime.py deleted file mode 100644 index 58a540b..0000000 --- a/venv/lib/python3.11/site-packages/jinja2/runtime.py +++ /dev/null @@ -1,1051 +0,0 @@ -"""The runtime functions and state used by compiled templates.""" -import functools -import sys -import typing as t -from collections import abc -from itertools import chain - -from markupsafe import escape # noqa: F401 -from markupsafe import Markup -from markupsafe import soft_str - -from .async_utils import auto_aiter -from .async_utils import auto_await # noqa: F401 -from .exceptions import TemplateNotFound # noqa: F401 -from .exceptions import TemplateRuntimeError # noqa: F401 -from .exceptions import UndefinedError -from .nodes import EvalContext -from .utils import _PassArg -from .utils import concat -from .utils import internalcode -from .utils import missing -from .utils import Namespace # noqa: F401 -from .utils import object_type_repr -from .utils import pass_eval_context - -V = t.TypeVar("V") -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) - -if t.TYPE_CHECKING: - import logging - import typing_extensions as te - from .environment import Environment - - class LoopRenderFunc(te.Protocol): - def __call__( - self, - reciter: t.Iterable[V], - loop_render_func: "LoopRenderFunc", - depth: int = 0, - ) -> str: - ... - - -# these variables are exported to the template runtime -exported = [ - "LoopContext", - "TemplateReference", - "Macro", - "Markup", - "TemplateRuntimeError", - "missing", - "escape", - "markup_join", - "str_join", - "identity", - "TemplateNotFound", - "Namespace", - "Undefined", - "internalcode", -] -async_exported = [ - "AsyncLoopContext", - "auto_aiter", - "auto_await", -] - - -def identity(x: V) -> V: - """Returns its argument. Useful for certain things in the - environment. - """ - return x - - -def markup_join(seq: t.Iterable[t.Any]) -> str: - """Concatenation that escapes if necessary and converts to string.""" - buf = [] - iterator = map(soft_str, seq) - for arg in iterator: - buf.append(arg) - if hasattr(arg, "__html__"): - return Markup("").join(chain(buf, iterator)) - return concat(buf) - - -def str_join(seq: t.Iterable[t.Any]) -> str: - """Simple args to string conversion and concatenation.""" - return concat(map(str, seq)) - - -def new_context( - environment: "Environment", - template_name: t.Optional[str], - blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]], - vars: t.Optional[t.Dict[str, t.Any]] = None, - shared: bool = False, - globals: t.Optional[t.MutableMapping[str, t.Any]] = None, - locals: t.Optional[t.Mapping[str, t.Any]] = None, -) -> "Context": - """Internal helper for context creation.""" - if vars is None: - vars = {} - if shared: - parent = vars - else: - parent = dict(globals or (), **vars) - if locals: - # if the parent is shared a copy should be created because - # we don't want to modify the dict passed - if shared: - parent = dict(parent) - for key, value in locals.items(): - if value is not missing: - parent[key] = value - return environment.context_class( - environment, parent, template_name, blocks, globals=globals - ) - - -class TemplateReference: - """The `self` in templates.""" - - def __init__(self, context: "Context") -> None: - self.__context = context - - def __getitem__(self, name: str) -> t.Any: - blocks = self.__context.blocks[name] - return BlockReference(name, self.__context, blocks, 0) - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.__context.name!r}>" - - -def _dict_method_all(dict_method: F) -> F: - @functools.wraps(dict_method) - def f_all(self: "Context") -> t.Any: - return dict_method(self.get_all()) - - return t.cast(F, f_all) - - -@abc.Mapping.register -class Context: - """The template context holds the variables of a template. It stores the - values passed to the template and also the names the template exports. - Creating instances is neither supported nor useful as it's created - automatically at various stages of the template evaluation and should not - be created by hand. - - The context is immutable. Modifications on :attr:`parent` **must not** - happen and modifications on :attr:`vars` are allowed from generated - template code only. Template filters and global functions marked as - :func:`pass_context` get the active context passed as first argument - and are allowed to access the context read-only. - - The template context supports read only dict operations (`get`, - `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, - `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` - method that doesn't fail with a `KeyError` but returns an - :class:`Undefined` object for missing variables. - """ - - def __init__( - self, - environment: "Environment", - parent: t.Dict[str, t.Any], - name: t.Optional[str], - blocks: t.Dict[str, t.Callable[["Context"], t.Iterator[str]]], - globals: t.Optional[t.MutableMapping[str, t.Any]] = None, - ): - self.parent = parent - self.vars: t.Dict[str, t.Any] = {} - self.environment: "Environment" = environment - self.eval_ctx = EvalContext(self.environment, name) - self.exported_vars: t.Set[str] = set() - self.name = name - self.globals_keys = set() if globals is None else set(globals) - - # create the initial mapping of blocks. Whenever template inheritance - # takes place the runtime will update this mapping with the new blocks - # from the template. - self.blocks = {k: [v] for k, v in blocks.items()} - - def super( - self, name: str, current: t.Callable[["Context"], t.Iterator[str]] - ) -> t.Union["BlockReference", "Undefined"]: - """Render a parent block.""" - try: - blocks = self.blocks[name] - index = blocks.index(current) + 1 - blocks[index] - except LookupError: - return self.environment.undefined( - f"there is no parent block called {name!r}.", name="super" - ) - return BlockReference(name, self, blocks, index) - - def get(self, key: str, default: t.Any = None) -> t.Any: - """Look up a variable by name, or return a default if the key is - not found. - - :param key: The variable name to look up. - :param default: The value to return if the key is not found. - """ - try: - return self[key] - except KeyError: - return default - - def resolve(self, key: str) -> t.Union[t.Any, "Undefined"]: - """Look up a variable by name, or return an :class:`Undefined` - object if the key is not found. - - If you need to add custom behavior, override - :meth:`resolve_or_missing`, not this method. The various lookup - functions use that method, not this one. - - :param key: The variable name to look up. - """ - rv = self.resolve_or_missing(key) - - if rv is missing: - return self.environment.undefined(name=key) - - return rv - - def resolve_or_missing(self, key: str) -> t.Any: - """Look up a variable by name, or return a ``missing`` sentinel - if the key is not found. - - Override this method to add custom lookup behavior. - :meth:`resolve`, :meth:`get`, and :meth:`__getitem__` use this - method. Don't call this method directly. - - :param key: The variable name to look up. - """ - if key in self.vars: - return self.vars[key] - - if key in self.parent: - return self.parent[key] - - return missing - - def get_exported(self) -> t.Dict[str, t.Any]: - """Get a new dict with the exported variables.""" - return {k: self.vars[k] for k in self.exported_vars} - - def get_all(self) -> t.Dict[str, t.Any]: - """Return the complete context as dict including the exported - variables. For optimizations reasons this might not return an - actual copy so be careful with using it. - """ - if not self.vars: - return self.parent - if not self.parent: - return self.vars - return dict(self.parent, **self.vars) - - @internalcode - def call( - __self, __obj: t.Callable, *args: t.Any, **kwargs: t.Any # noqa: B902 - ) -> t.Union[t.Any, "Undefined"]: - """Call the callable with the arguments and keyword arguments - provided but inject the active context or environment as first - argument if the callable has :func:`pass_context` or - :func:`pass_environment`. - """ - if __debug__: - __traceback_hide__ = True # noqa - - # Allow callable classes to take a context - if ( - hasattr(__obj, "__call__") # noqa: B004 - and _PassArg.from_obj(__obj.__call__) is not None - ): - __obj = __obj.__call__ - - pass_arg = _PassArg.from_obj(__obj) - - if pass_arg is _PassArg.context: - # the active context should have access to variables set in - # loops and blocks without mutating the context itself - if kwargs.get("_loop_vars"): - __self = __self.derived(kwargs["_loop_vars"]) - if kwargs.get("_block_vars"): - __self = __self.derived(kwargs["_block_vars"]) - args = (__self,) + args - elif pass_arg is _PassArg.eval_context: - args = (__self.eval_ctx,) + args - elif pass_arg is _PassArg.environment: - args = (__self.environment,) + args - - kwargs.pop("_block_vars", None) - kwargs.pop("_loop_vars", None) - - try: - return __obj(*args, **kwargs) - except StopIteration: - return __self.environment.undefined( - "value was undefined because a callable raised a" - " StopIteration exception" - ) - - def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> "Context": - """Internal helper function to create a derived context. This is - used in situations where the system needs a new context in the same - template that is independent. - """ - context = new_context( - self.environment, self.name, {}, self.get_all(), True, None, locals - ) - context.eval_ctx = self.eval_ctx - context.blocks.update((k, list(v)) for k, v in self.blocks.items()) - return context - - keys = _dict_method_all(dict.keys) - values = _dict_method_all(dict.values) - items = _dict_method_all(dict.items) - - def __contains__(self, name: str) -> bool: - return name in self.vars or name in self.parent - - def __getitem__(self, key: str) -> t.Any: - """Look up a variable by name with ``[]`` syntax, or raise a - ``KeyError`` if the key is not found. - """ - item = self.resolve_or_missing(key) - - if item is missing: - raise KeyError(key) - - return item - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.get_all()!r} of {self.name!r}>" - - -class BlockReference: - """One block on a template reference.""" - - def __init__( - self, - name: str, - context: "Context", - stack: t.List[t.Callable[["Context"], t.Iterator[str]]], - depth: int, - ) -> None: - self.name = name - self._context = context - self._stack = stack - self._depth = depth - - @property - def super(self) -> t.Union["BlockReference", "Undefined"]: - """Super the block.""" - if self._depth + 1 >= len(self._stack): - return self._context.environment.undefined( - f"there is no parent block called {self.name!r}.", name="super" - ) - return BlockReference(self.name, self._context, self._stack, self._depth + 1) - - @internalcode - async def _async_call(self) -> str: - rv = concat( - [x async for x in self._stack[self._depth](self._context)] # type: ignore - ) - - if self._context.eval_ctx.autoescape: - return Markup(rv) - - return rv - - @internalcode - def __call__(self) -> str: - if self._context.environment.is_async: - return self._async_call() # type: ignore - - rv = concat(self._stack[self._depth](self._context)) - - if self._context.eval_ctx.autoescape: - return Markup(rv) - - return rv - - -class LoopContext: - """A wrapper iterable for dynamic ``for`` loops, with information - about the loop and iteration. - """ - - #: Current iteration of the loop, starting at 0. - index0 = -1 - - _length: t.Optional[int] = None - _after: t.Any = missing - _current: t.Any = missing - _before: t.Any = missing - _last_changed_value: t.Any = missing - - def __init__( - self, - iterable: t.Iterable[V], - undefined: t.Type["Undefined"], - recurse: t.Optional["LoopRenderFunc"] = None, - depth0: int = 0, - ) -> None: - """ - :param iterable: Iterable to wrap. - :param undefined: :class:`Undefined` class to use for next and - previous items. - :param recurse: The function to render the loop body when the - loop is marked recursive. - :param depth0: Incremented when looping recursively. - """ - self._iterable = iterable - self._iterator = self._to_iterator(iterable) - self._undefined = undefined - self._recurse = recurse - #: How many levels deep a recursive loop currently is, starting at 0. - self.depth0 = depth0 - - @staticmethod - def _to_iterator(iterable: t.Iterable[V]) -> t.Iterator[V]: - return iter(iterable) - - @property - def length(self) -> int: - """Length of the iterable. - - If the iterable is a generator or otherwise does not have a - size, it is eagerly evaluated to get a size. - """ - if self._length is not None: - return self._length - - try: - self._length = len(self._iterable) # type: ignore - except TypeError: - iterable = list(self._iterator) - self._iterator = self._to_iterator(iterable) - self._length = len(iterable) + self.index + (self._after is not missing) - - return self._length - - def __len__(self) -> int: - return self.length - - @property - def depth(self) -> int: - """How many levels deep a recursive loop currently is, starting at 1.""" - return self.depth0 + 1 - - @property - def index(self) -> int: - """Current iteration of the loop, starting at 1.""" - return self.index0 + 1 - - @property - def revindex0(self) -> int: - """Number of iterations from the end of the loop, ending at 0. - - Requires calculating :attr:`length`. - """ - return self.length - self.index - - @property - def revindex(self) -> int: - """Number of iterations from the end of the loop, ending at 1. - - Requires calculating :attr:`length`. - """ - return self.length - self.index0 - - @property - def first(self) -> bool: - """Whether this is the first iteration of the loop.""" - return self.index0 == 0 - - def _peek_next(self) -> t.Any: - """Return the next element in the iterable, or :data:`missing` - if the iterable is exhausted. Only peeks one item ahead, caching - the result in :attr:`_last` for use in subsequent checks. The - cache is reset when :meth:`__next__` is called. - """ - if self._after is not missing: - return self._after - - self._after = next(self._iterator, missing) - return self._after - - @property - def last(self) -> bool: - """Whether this is the last iteration of the loop. - - Causes the iterable to advance early. See - :func:`itertools.groupby` for issues this can cause. - The :func:`groupby` filter avoids that issue. - """ - return self._peek_next() is missing - - @property - def previtem(self) -> t.Union[t.Any, "Undefined"]: - """The item in the previous iteration. Undefined during the - first iteration. - """ - if self.first: - return self._undefined("there is no previous item") - - return self._before - - @property - def nextitem(self) -> t.Union[t.Any, "Undefined"]: - """The item in the next iteration. Undefined during the last - iteration. - - Causes the iterable to advance early. See - :func:`itertools.groupby` for issues this can cause. - The :func:`jinja-filters.groupby` filter avoids that issue. - """ - rv = self._peek_next() - - if rv is missing: - return self._undefined("there is no next item") - - return rv - - def cycle(self, *args: V) -> V: - """Return a value from the given args, cycling through based on - the current :attr:`index0`. - - :param args: One or more values to cycle through. - """ - if not args: - raise TypeError("no items for cycling given") - - return args[self.index0 % len(args)] - - def changed(self, *value: t.Any) -> bool: - """Return ``True`` if previously called with a different value - (including when called for the first time). - - :param value: One or more values to compare to the last call. - """ - if self._last_changed_value != value: - self._last_changed_value = value - return True - - return False - - def __iter__(self) -> "LoopContext": - return self - - def __next__(self) -> t.Tuple[t.Any, "LoopContext"]: - if self._after is not missing: - rv = self._after - self._after = missing - else: - rv = next(self._iterator) - - self.index0 += 1 - self._before = self._current - self._current = rv - return rv, self - - @internalcode - def __call__(self, iterable: t.Iterable[V]) -> str: - """When iterating over nested data, render the body of the loop - recursively with the given inner iterable data. - - The loop must have the ``recursive`` marker for this to work. - """ - if self._recurse is None: - raise TypeError( - "The loop must have the 'recursive' marker to be called recursively." - ) - - return self._recurse(iterable, self._recurse, depth=self.depth) - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.index}/{self.length}>" - - -class AsyncLoopContext(LoopContext): - _iterator: t.AsyncIterator[t.Any] # type: ignore - - @staticmethod - def _to_iterator( # type: ignore - iterable: t.Union[t.Iterable[V], t.AsyncIterable[V]] - ) -> t.AsyncIterator[V]: - return auto_aiter(iterable) - - @property - async def length(self) -> int: # type: ignore - if self._length is not None: - return self._length - - try: - self._length = len(self._iterable) # type: ignore - except TypeError: - iterable = [x async for x in self._iterator] - self._iterator = self._to_iterator(iterable) - self._length = len(iterable) + self.index + (self._after is not missing) - - return self._length - - @property - async def revindex0(self) -> int: # type: ignore - return await self.length - self.index - - @property - async def revindex(self) -> int: # type: ignore - return await self.length - self.index0 - - async def _peek_next(self) -> t.Any: - if self._after is not missing: - return self._after - - try: - self._after = await self._iterator.__anext__() - except StopAsyncIteration: - self._after = missing - - return self._after - - @property - async def last(self) -> bool: # type: ignore - return await self._peek_next() is missing - - @property - async def nextitem(self) -> t.Union[t.Any, "Undefined"]: - rv = await self._peek_next() - - if rv is missing: - return self._undefined("there is no next item") - - return rv - - def __aiter__(self) -> "AsyncLoopContext": - return self - - async def __anext__(self) -> t.Tuple[t.Any, "AsyncLoopContext"]: - if self._after is not missing: - rv = self._after - self._after = missing - else: - rv = await self._iterator.__anext__() - - self.index0 += 1 - self._before = self._current - self._current = rv - return rv, self - - -class Macro: - """Wraps a macro function.""" - - def __init__( - self, - environment: "Environment", - func: t.Callable[..., str], - name: str, - arguments: t.List[str], - catch_kwargs: bool, - catch_varargs: bool, - caller: bool, - default_autoescape: t.Optional[bool] = None, - ): - self._environment = environment - self._func = func - self._argument_count = len(arguments) - self.name = name - self.arguments = arguments - self.catch_kwargs = catch_kwargs - self.catch_varargs = catch_varargs - self.caller = caller - self.explicit_caller = "caller" in arguments - - if default_autoescape is None: - if callable(environment.autoescape): - default_autoescape = environment.autoescape(None) - else: - default_autoescape = environment.autoescape - - self._default_autoescape = default_autoescape - - @internalcode - @pass_eval_context - def __call__(self, *args: t.Any, **kwargs: t.Any) -> str: - # This requires a bit of explanation, In the past we used to - # decide largely based on compile-time information if a macro is - # safe or unsafe. While there was a volatile mode it was largely - # unused for deciding on escaping. This turns out to be - # problematic for macros because whether a macro is safe depends not - # on the escape mode when it was defined, but rather when it was used. - # - # Because however we export macros from the module system and - # there are historic callers that do not pass an eval context (and - # will continue to not pass one), we need to perform an instance - # check here. - # - # This is considered safe because an eval context is not a valid - # argument to callables otherwise anyway. Worst case here is - # that if no eval context is passed we fall back to the compile - # time autoescape flag. - if args and isinstance(args[0], EvalContext): - autoescape = args[0].autoescape - args = args[1:] - else: - autoescape = self._default_autoescape - - # try to consume the positional arguments - arguments = list(args[: self._argument_count]) - off = len(arguments) - - # For information why this is necessary refer to the handling - # of caller in the `macro_body` handler in the compiler. - found_caller = False - - # if the number of arguments consumed is not the number of - # arguments expected we start filling in keyword arguments - # and defaults. - if off != self._argument_count: - for name in self.arguments[len(arguments) :]: - try: - value = kwargs.pop(name) - except KeyError: - value = missing - if name == "caller": - found_caller = True - arguments.append(value) - else: - found_caller = self.explicit_caller - - # it's important that the order of these arguments does not change - # if not also changed in the compiler's `function_scoping` method. - # the order is caller, keyword arguments, positional arguments! - if self.caller and not found_caller: - caller = kwargs.pop("caller", None) - if caller is None: - caller = self._environment.undefined("No caller defined", name="caller") - arguments.append(caller) - - if self.catch_kwargs: - arguments.append(kwargs) - elif kwargs: - if "caller" in kwargs: - raise TypeError( - f"macro {self.name!r} was invoked with two values for the special" - " caller argument. This is most likely a bug." - ) - raise TypeError( - f"macro {self.name!r} takes no keyword argument {next(iter(kwargs))!r}" - ) - if self.catch_varargs: - arguments.append(args[self._argument_count :]) - elif len(args) > self._argument_count: - raise TypeError( - f"macro {self.name!r} takes not more than" - f" {len(self.arguments)} argument(s)" - ) - - return self._invoke(arguments, autoescape) - - async def _async_invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str: - rv = await self._func(*arguments) # type: ignore - - if autoescape: - return Markup(rv) - - return rv # type: ignore - - def _invoke(self, arguments: t.List[t.Any], autoescape: bool) -> str: - if self._environment.is_async: - return self._async_invoke(arguments, autoescape) # type: ignore - - rv = self._func(*arguments) - - if autoescape: - rv = Markup(rv) - - return rv - - def __repr__(self) -> str: - name = "anonymous" if self.name is None else repr(self.name) - return f"<{type(self).__name__} {name}>" - - -class Undefined: - """The default undefined type. This undefined type can be printed and - iterated over, but every other access will raise an :exc:`UndefinedError`: - - >>> foo = Undefined(name='foo') - >>> str(foo) - '' - >>> not foo - True - >>> foo + 42 - Traceback (most recent call last): - ... - jinja2.exceptions.UndefinedError: 'foo' is undefined - """ - - __slots__ = ( - "_undefined_hint", - "_undefined_obj", - "_undefined_name", - "_undefined_exception", - ) - - def __init__( - self, - hint: t.Optional[str] = None, - obj: t.Any = missing, - name: t.Optional[str] = None, - exc: t.Type[TemplateRuntimeError] = UndefinedError, - ) -> None: - self._undefined_hint = hint - self._undefined_obj = obj - self._undefined_name = name - self._undefined_exception = exc - - @property - def _undefined_message(self) -> str: - """Build a message about the undefined value based on how it was - accessed. - """ - if self._undefined_hint: - return self._undefined_hint - - if self._undefined_obj is missing: - return f"{self._undefined_name!r} is undefined" - - if not isinstance(self._undefined_name, str): - return ( - f"{object_type_repr(self._undefined_obj)} has no" - f" element {self._undefined_name!r}" - ) - - return ( - f"{object_type_repr(self._undefined_obj)!r} has no" - f" attribute {self._undefined_name!r}" - ) - - @internalcode - def _fail_with_undefined_error( - self, *args: t.Any, **kwargs: t.Any - ) -> "te.NoReturn": - """Raise an :exc:`UndefinedError` when operations are performed - on the undefined value. - """ - raise self._undefined_exception(self._undefined_message) - - @internalcode - def __getattr__(self, name: str) -> t.Any: - if name[:2] == "__": - raise AttributeError(name) - - return self._fail_with_undefined_error() - - __add__ = __radd__ = __sub__ = __rsub__ = _fail_with_undefined_error - __mul__ = __rmul__ = __div__ = __rdiv__ = _fail_with_undefined_error - __truediv__ = __rtruediv__ = _fail_with_undefined_error - __floordiv__ = __rfloordiv__ = _fail_with_undefined_error - __mod__ = __rmod__ = _fail_with_undefined_error - __pos__ = __neg__ = _fail_with_undefined_error - __call__ = __getitem__ = _fail_with_undefined_error - __lt__ = __le__ = __gt__ = __ge__ = _fail_with_undefined_error - __int__ = __float__ = __complex__ = _fail_with_undefined_error - __pow__ = __rpow__ = _fail_with_undefined_error - - def __eq__(self, other: t.Any) -> bool: - return type(self) is type(other) - - def __ne__(self, other: t.Any) -> bool: - return not self.__eq__(other) - - def __hash__(self) -> int: - return id(type(self)) - - def __str__(self) -> str: - return "" - - def __len__(self) -> int: - return 0 - - def __iter__(self) -> t.Iterator[t.Any]: - yield from () - - async def __aiter__(self) -> t.AsyncIterator[t.Any]: - for _ in (): - yield - - def __bool__(self) -> bool: - return False - - def __repr__(self) -> str: - return "Undefined" - - -def make_logging_undefined( - logger: t.Optional["logging.Logger"] = None, base: t.Type[Undefined] = Undefined -) -> t.Type[Undefined]: - """Given a logger object this returns a new undefined class that will - log certain failures. It will log iterations and printing. If no - logger is given a default logger is created. - - Example:: - - logger = logging.getLogger(__name__) - LoggingUndefined = make_logging_undefined( - logger=logger, - base=Undefined - ) - - .. versionadded:: 2.8 - - :param logger: the logger to use. If not provided, a default logger - is created. - :param base: the base class to add logging functionality to. This - defaults to :class:`Undefined`. - """ - if logger is None: - import logging - - logger = logging.getLogger(__name__) - logger.addHandler(logging.StreamHandler(sys.stderr)) - - def _log_message(undef: Undefined) -> None: - logger.warning("Template variable warning: %s", undef._undefined_message) - - class LoggingUndefined(base): # type: ignore - __slots__ = () - - def _fail_with_undefined_error( # type: ignore - self, *args: t.Any, **kwargs: t.Any - ) -> "te.NoReturn": - try: - super()._fail_with_undefined_error(*args, **kwargs) - except self._undefined_exception as e: - logger.error("Template variable error: %s", e) # type: ignore - raise e - - def __str__(self) -> str: - _log_message(self) - return super().__str__() # type: ignore - - def __iter__(self) -> t.Iterator[t.Any]: - _log_message(self) - return super().__iter__() # type: ignore - - def __bool__(self) -> bool: - _log_message(self) - return super().__bool__() # type: ignore - - return LoggingUndefined - - -class ChainableUndefined(Undefined): - """An undefined that is chainable, where both ``__getattr__`` and - ``__getitem__`` return itself rather than raising an - :exc:`UndefinedError`. - - >>> foo = ChainableUndefined(name='foo') - >>> str(foo.bar['baz']) - '' - >>> foo.bar['baz'] + 42 - Traceback (most recent call last): - ... - jinja2.exceptions.UndefinedError: 'foo' is undefined - - .. versionadded:: 2.11.0 - """ - - __slots__ = () - - def __html__(self) -> str: - return str(self) - - def __getattr__(self, _: str) -> "ChainableUndefined": - return self - - __getitem__ = __getattr__ # type: ignore - - -class DebugUndefined(Undefined): - """An undefined that returns the debug info when printed. - - >>> foo = DebugUndefined(name='foo') - >>> str(foo) - '{{ foo }}' - >>> not foo - True - >>> foo + 42 - Traceback (most recent call last): - ... - jinja2.exceptions.UndefinedError: 'foo' is undefined - """ - - __slots__ = () - - def __str__(self) -> str: - if self._undefined_hint: - message = f"undefined value printed: {self._undefined_hint}" - - elif self._undefined_obj is missing: - message = self._undefined_name # type: ignore - - else: - message = ( - f"no such element: {object_type_repr(self._undefined_obj)}" - f"[{self._undefined_name!r}]" - ) - - return f"{{{{ {message} }}}}" - - -class StrictUndefined(Undefined): - """An undefined that barks on print and iteration as well as boolean - tests and all kinds of comparisons. In other words: you can do nothing - with it except checking if it's defined using the `defined` test. - - >>> foo = StrictUndefined(name='foo') - >>> str(foo) - Traceback (most recent call last): - ... - jinja2.exceptions.UndefinedError: 'foo' is undefined - >>> not foo - Traceback (most recent call last): - ... - jinja2.exceptions.UndefinedError: 'foo' is undefined - >>> foo + 42 - Traceback (most recent call last): - ... - jinja2.exceptions.UndefinedError: 'foo' is undefined - """ - - __slots__ = () - __iter__ = __str__ = __len__ = Undefined._fail_with_undefined_error - __eq__ = __ne__ = __bool__ = __hash__ = Undefined._fail_with_undefined_error - __contains__ = Undefined._fail_with_undefined_error - - -# Remove slots attributes, after the metaclass is applied they are -# unneeded and contain wrong data for subclasses. -del ( - Undefined.__slots__, - ChainableUndefined.__slots__, - DebugUndefined.__slots__, - StrictUndefined.__slots__, -) |