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