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