1# -*- coding: utf-8 -*- 2""" 3 jinja2.runtime 4 ~~~~~~~~~~~~~~ 5 6 Runtime helpers. 7 8 :copyright: (c) 2017 by the Jinja Team. 9 :license: BSD. 10""" 11import sys 12 13from itertools import chain 14from types import MethodType 15 16from jinja2.nodes import EvalContext, _context_function_types 17from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ 18 internalcode, object_type_repr, evalcontextfunction, Namespace 19from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ 20 TemplateNotFound 21from jinja2._compat import imap, text_type, iteritems, \ 22 implements_iterator, implements_to_string, string_types, PY2, \ 23 with_metaclass 24 25 26# these variables are exported to the template runtime 27__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', 28 'TemplateRuntimeError', 'missing', 'concat', 'escape', 29 'markup_join', 'unicode_join', 'to_string', 'identity', 30 'TemplateNotFound', 'Namespace'] 31 32#: the name of the function that is used to convert something into 33#: a string. We can just use the text type here. 34to_string = text_type 35 36#: the identity function. Useful for certain things in the environment 37identity = lambda x: x 38 39_first_iteration = object() 40_last_iteration = object() 41 42 43def markup_join(seq): 44 """Concatenation that escapes if necessary and converts to unicode.""" 45 buf = [] 46 iterator = imap(soft_unicode, seq) 47 for arg in iterator: 48 buf.append(arg) 49 if hasattr(arg, '__html__'): 50 return Markup(u'').join(chain(buf, iterator)) 51 return concat(buf) 52 53 54def unicode_join(seq): 55 """Simple args to unicode conversion and concatenation.""" 56 return concat(imap(text_type, seq)) 57 58 59def new_context(environment, template_name, blocks, vars=None, 60 shared=None, globals=None, locals=None): 61 """Internal helper to for context creation.""" 62 if vars is None: 63 vars = {} 64 if shared: 65 parent = vars 66 else: 67 parent = dict(globals or (), **vars) 68 if locals: 69 # if the parent is shared a copy should be created because 70 # we don't want to modify the dict passed 71 if shared: 72 parent = dict(parent) 73 for key, value in iteritems(locals): 74 if value is not missing: 75 parent[key] = value 76 return environment.context_class(environment, parent, template_name, 77 blocks) 78 79 80class TemplateReference(object): 81 """The `self` in templates.""" 82 83 def __init__(self, context): 84 self.__context = context 85 86 def __getitem__(self, name): 87 blocks = self.__context.blocks[name] 88 return BlockReference(name, self.__context, blocks, 0) 89 90 def __repr__(self): 91 return '<%s %r>' % ( 92 self.__class__.__name__, 93 self.__context.name 94 ) 95 96 97def _get_func(x): 98 return getattr(x, '__func__', x) 99 100 101class ContextMeta(type): 102 103 def __new__(cls, name, bases, d): 104 rv = type.__new__(cls, name, bases, d) 105 if bases == (): 106 return rv 107 108 resolve = _get_func(rv.resolve) 109 default_resolve = _get_func(Context.resolve) 110 resolve_or_missing = _get_func(rv.resolve_or_missing) 111 default_resolve_or_missing = _get_func(Context.resolve_or_missing) 112 113 # If we have a changed resolve but no changed default or missing 114 # resolve we invert the call logic. 115 if resolve is not default_resolve and \ 116 resolve_or_missing is default_resolve_or_missing: 117 rv._legacy_resolve_mode = True 118 elif resolve is default_resolve and \ 119 resolve_or_missing is default_resolve_or_missing: 120 rv._fast_resolve_mode = True 121 122 return rv 123 124 125def resolve_or_missing(context, key, missing=missing): 126 if key in context.vars: 127 return context.vars[key] 128 if key in context.parent: 129 return context.parent[key] 130 return missing 131 132 133class Context(with_metaclass(ContextMeta)): 134 """The template context holds the variables of a template. It stores the 135 values passed to the template and also the names the template exports. 136 Creating instances is neither supported nor useful as it's created 137 automatically at various stages of the template evaluation and should not 138 be created by hand. 139 140 The context is immutable. Modifications on :attr:`parent` **must not** 141 happen and modifications on :attr:`vars` are allowed from generated 142 template code only. Template filters and global functions marked as 143 :func:`contextfunction`\\s get the active context passed as first argument 144 and are allowed to access the context read-only. 145 146 The template context supports read only dict operations (`get`, 147 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, 148 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` 149 method that doesn't fail with a `KeyError` but returns an 150 :class:`Undefined` object for missing variables. 151 """ 152 # XXX: we want to eventually make this be a deprecation warning and 153 # remove it. 154 _legacy_resolve_mode = False 155 _fast_resolve_mode = False 156 157 def __init__(self, environment, parent, name, blocks): 158 self.parent = parent 159 self.vars = {} 160 self.environment = environment 161 self.eval_ctx = EvalContext(self.environment, name) 162 self.exported_vars = set() 163 self.name = name 164 165 # create the initial mapping of blocks. Whenever template inheritance 166 # takes place the runtime will update this mapping with the new blocks 167 # from the template. 168 self.blocks = dict((k, [v]) for k, v in iteritems(blocks)) 169 170 # In case we detect the fast resolve mode we can set up an alias 171 # here that bypasses the legacy code logic. 172 if self._fast_resolve_mode: 173 self.resolve_or_missing = MethodType(resolve_or_missing, self) 174 175 def super(self, name, current): 176 """Render a parent block.""" 177 try: 178 blocks = self.blocks[name] 179 index = blocks.index(current) + 1 180 blocks[index] 181 except LookupError: 182 return self.environment.undefined('there is no parent block ' 183 'called %r.' % name, 184 name='super') 185 return BlockReference(name, self, blocks, index) 186 187 def get(self, key, default=None): 188 """Returns an item from the template context, if it doesn't exist 189 `default` is returned. 190 """ 191 try: 192 return self[key] 193 except KeyError: 194 return default 195 196 def resolve(self, key): 197 """Looks up a variable like `__getitem__` or `get` but returns an 198 :class:`Undefined` object with the name of the name looked up. 199 """ 200 if self._legacy_resolve_mode: 201 rv = resolve_or_missing(self, key) 202 else: 203 rv = self.resolve_or_missing(key) 204 if rv is missing: 205 return self.environment.undefined(name=key) 206 return rv 207 208 def resolve_or_missing(self, key): 209 """Resolves a variable like :meth:`resolve` but returns the 210 special `missing` value if it cannot be found. 211 """ 212 if self._legacy_resolve_mode: 213 rv = self.resolve(key) 214 if isinstance(rv, Undefined): 215 rv = missing 216 return rv 217 return resolve_or_missing(self, key) 218 219 def get_exported(self): 220 """Get a new dict with the exported variables.""" 221 return dict((k, self.vars[k]) for k in self.exported_vars) 222 223 def get_all(self): 224 """Return the complete context as dict including the exported 225 variables. For optimizations reasons this might not return an 226 actual copy so be careful with using it. 227 """ 228 if not self.vars: 229 return self.parent 230 if not self.parent: 231 return self.vars 232 return dict(self.parent, **self.vars) 233 234 @internalcode 235 def call(__self, __obj, *args, **kwargs): 236 """Call the callable with the arguments and keyword arguments 237 provided but inject the active context or environment as first 238 argument if the callable is a :func:`contextfunction` or 239 :func:`environmentfunction`. 240 """ 241 if __debug__: 242 __traceback_hide__ = True # noqa 243 244 # Allow callable classes to take a context 245 if hasattr(__obj, '__call__'): 246 fn = __obj.__call__ 247 for fn_type in ('contextfunction', 248 'evalcontextfunction', 249 'environmentfunction'): 250 if hasattr(fn, fn_type): 251 __obj = fn 252 break 253 254 if isinstance(__obj, _context_function_types): 255 if getattr(__obj, 'contextfunction', 0): 256 args = (__self,) + args 257 elif getattr(__obj, 'evalcontextfunction', 0): 258 args = (__self.eval_ctx,) + args 259 elif getattr(__obj, 'environmentfunction', 0): 260 args = (__self.environment,) + args 261 try: 262 return __obj(*args, **kwargs) 263 except StopIteration: 264 return __self.environment.undefined('value was undefined because ' 265 'a callable raised a ' 266 'StopIteration exception') 267 268 def derived(self, locals=None): 269 """Internal helper function to create a derived context. This is 270 used in situations where the system needs a new context in the same 271 template that is independent. 272 """ 273 context = new_context(self.environment, self.name, {}, 274 self.get_all(), True, None, locals) 275 context.eval_ctx = self.eval_ctx 276 context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks)) 277 return context 278 279 def _all(meth): 280 proxy = lambda self: getattr(self.get_all(), meth)() 281 proxy.__doc__ = getattr(dict, meth).__doc__ 282 proxy.__name__ = meth 283 return proxy 284 285 keys = _all('keys') 286 values = _all('values') 287 items = _all('items') 288 289 # not available on python 3 290 if PY2: 291 iterkeys = _all('iterkeys') 292 itervalues = _all('itervalues') 293 iteritems = _all('iteritems') 294 del _all 295 296 def __contains__(self, name): 297 return name in self.vars or name in self.parent 298 299 def __getitem__(self, key): 300 """Lookup a variable or raise `KeyError` if the variable is 301 undefined. 302 """ 303 item = self.resolve_or_missing(key) 304 if item is missing: 305 raise KeyError(key) 306 return item 307 308 def __repr__(self): 309 return '<%s %s of %r>' % ( 310 self.__class__.__name__, 311 repr(self.get_all()), 312 self.name 313 ) 314 315 316# register the context as mapping if possible 317try: 318 from collections import Mapping 319 Mapping.register(Context) 320except ImportError: 321 pass 322 323 324class BlockReference(object): 325 """One block on a template reference.""" 326 327 def __init__(self, name, context, stack, depth): 328 self.name = name 329 self._context = context 330 self._stack = stack 331 self._depth = depth 332 333 @property 334 def super(self): 335 """Super the block.""" 336 if self._depth + 1 >= len(self._stack): 337 return self._context.environment. \ 338 undefined('there is no parent block called %r.' % 339 self.name, name='super') 340 return BlockReference(self.name, self._context, self._stack, 341 self._depth + 1) 342 343 @internalcode 344 def __call__(self): 345 rv = concat(self._stack[self._depth](self._context)) 346 if self._context.eval_ctx.autoescape: 347 rv = Markup(rv) 348 return rv 349 350 351class LoopContextBase(object): 352 """A loop context for dynamic iteration.""" 353 354 _before = _first_iteration 355 _current = _first_iteration 356 _after = _last_iteration 357 _length = None 358 359 def __init__(self, undefined, recurse=None, depth0=0): 360 self._undefined = undefined 361 self._recurse = recurse 362 self.index0 = -1 363 self.depth0 = depth0 364 self._last_checked_value = missing 365 366 def cycle(self, *args): 367 """Cycles among the arguments with the current loop index.""" 368 if not args: 369 raise TypeError('no items for cycling given') 370 return args[self.index0 % len(args)] 371 372 def changed(self, *value): 373 """Checks whether the value has changed since the last call.""" 374 if self._last_checked_value != value: 375 self._last_checked_value = value 376 return True 377 return False 378 379 first = property(lambda x: x.index0 == 0) 380 last = property(lambda x: x._after is _last_iteration) 381 index = property(lambda x: x.index0 + 1) 382 revindex = property(lambda x: x.length - x.index0) 383 revindex0 = property(lambda x: x.length - x.index) 384 depth = property(lambda x: x.depth0 + 1) 385 386 @property 387 def previtem(self): 388 if self._before is _first_iteration: 389 return self._undefined('there is no previous item') 390 return self._before 391 392 @property 393 def nextitem(self): 394 if self._after is _last_iteration: 395 return self._undefined('there is no next item') 396 return self._after 397 398 def __len__(self): 399 return self.length 400 401 @internalcode 402 def loop(self, iterable): 403 if self._recurse is None: 404 raise TypeError('Tried to call non recursive loop. Maybe you ' 405 "forgot the 'recursive' modifier.") 406 return self._recurse(iterable, self._recurse, self.depth0 + 1) 407 408 # a nifty trick to enhance the error message if someone tried to call 409 # the the loop without or with too many arguments. 410 __call__ = loop 411 del loop 412 413 def __repr__(self): 414 return '<%s %r/%r>' % ( 415 self.__class__.__name__, 416 self.index, 417 self.length 418 ) 419 420 421class LoopContext(LoopContextBase): 422 423 def __init__(self, iterable, undefined, recurse=None, depth0=0): 424 LoopContextBase.__init__(self, undefined, recurse, depth0) 425 self._iterator = iter(iterable) 426 427 # try to get the length of the iterable early. This must be done 428 # here because there are some broken iterators around where there 429 # __len__ is the number of iterations left (i'm looking at your 430 # listreverseiterator!). 431 try: 432 self._length = len(iterable) 433 except (TypeError, AttributeError): 434 self._length = None 435 self._after = self._safe_next() 436 437 @property 438 def length(self): 439 if self._length is None: 440 # if was not possible to get the length of the iterator when 441 # the loop context was created (ie: iterating over a generator) 442 # we have to convert the iterable into a sequence and use the 443 # length of that + the number of iterations so far. 444 iterable = tuple(self._iterator) 445 self._iterator = iter(iterable) 446 iterations_done = self.index0 + 2 447 self._length = len(iterable) + iterations_done 448 return self._length 449 450 def __iter__(self): 451 return LoopContextIterator(self) 452 453 def _safe_next(self): 454 try: 455 return next(self._iterator) 456 except StopIteration: 457 return _last_iteration 458 459 460@implements_iterator 461class LoopContextIterator(object): 462 """The iterator for a loop context.""" 463 __slots__ = ('context',) 464 465 def __init__(self, context): 466 self.context = context 467 468 def __iter__(self): 469 return self 470 471 def __next__(self): 472 ctx = self.context 473 ctx.index0 += 1 474 if ctx._after is _last_iteration: 475 raise StopIteration() 476 ctx._before = ctx._current 477 ctx._current = ctx._after 478 ctx._after = ctx._safe_next() 479 return ctx._current, ctx 480 481 482class Macro(object): 483 """Wraps a macro function.""" 484 485 def __init__(self, environment, func, name, arguments, 486 catch_kwargs, catch_varargs, caller, 487 default_autoescape=None): 488 self._environment = environment 489 self._func = func 490 self._argument_count = len(arguments) 491 self.name = name 492 self.arguments = arguments 493 self.catch_kwargs = catch_kwargs 494 self.catch_varargs = catch_varargs 495 self.caller = caller 496 self.explicit_caller = 'caller' in arguments 497 if default_autoescape is None: 498 default_autoescape = environment.autoescape 499 self._default_autoescape = default_autoescape 500 501 @internalcode 502 @evalcontextfunction 503 def __call__(self, *args, **kwargs): 504 # This requires a bit of explanation, In the past we used to 505 # decide largely based on compile-time information if a macro is 506 # safe or unsafe. While there was a volatile mode it was largely 507 # unused for deciding on escaping. This turns out to be 508 # problemtic for macros because if a macro is safe or not not so 509 # much depends on the escape mode when it was defined but when it 510 # was used. 511 # 512 # Because however we export macros from the module system and 513 # there are historic callers that do not pass an eval context (and 514 # will continue to not pass one), we need to perform an instance 515 # check here. 516 # 517 # This is considered safe because an eval context is not a valid 518 # argument to callables otherwise anwyays. Worst case here is 519 # that if no eval context is passed we fall back to the compile 520 # time autoescape flag. 521 if args and isinstance(args[0], EvalContext): 522 autoescape = args[0].autoescape 523 args = args[1:] 524 else: 525 autoescape = self._default_autoescape 526 527 # try to consume the positional arguments 528 arguments = list(args[:self._argument_count]) 529 off = len(arguments) 530 531 # For information why this is necessary refer to the handling 532 # of caller in the `macro_body` handler in the compiler. 533 found_caller = False 534 535 # if the number of arguments consumed is not the number of 536 # arguments expected we start filling in keyword arguments 537 # and defaults. 538 if off != self._argument_count: 539 for idx, name in enumerate(self.arguments[len(arguments):]): 540 try: 541 value = kwargs.pop(name) 542 except KeyError: 543 value = missing 544 if name == 'caller': 545 found_caller = True 546 arguments.append(value) 547 else: 548 found_caller = self.explicit_caller 549 550 # it's important that the order of these arguments does not change 551 # if not also changed in the compiler's `function_scoping` method. 552 # the order is caller, keyword arguments, positional arguments! 553 if self.caller and not found_caller: 554 caller = kwargs.pop('caller', None) 555 if caller is None: 556 caller = self._environment.undefined('No caller defined', 557 name='caller') 558 arguments.append(caller) 559 560 if self.catch_kwargs: 561 arguments.append(kwargs) 562 elif kwargs: 563 if 'caller' in kwargs: 564 raise TypeError('macro %r was invoked with two values for ' 565 'the special caller argument. This is ' 566 'most likely a bug.' % self.name) 567 raise TypeError('macro %r takes no keyword argument %r' % 568 (self.name, next(iter(kwargs)))) 569 if self.catch_varargs: 570 arguments.append(args[self._argument_count:]) 571 elif len(args) > self._argument_count: 572 raise TypeError('macro %r takes not more than %d argument(s)' % 573 (self.name, len(self.arguments))) 574 575 return self._invoke(arguments, autoescape) 576 577 def _invoke(self, arguments, autoescape): 578 """This method is being swapped out by the async implementation.""" 579 rv = self._func(*arguments) 580 if autoescape: 581 rv = Markup(rv) 582 return rv 583 584 def __repr__(self): 585 return '<%s %s>' % ( 586 self.__class__.__name__, 587 self.name is None and 'anonymous' or repr(self.name) 588 ) 589 590 591@implements_to_string 592class Undefined(object): 593 """The default undefined type. This undefined type can be printed and 594 iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`: 595 596 >>> foo = Undefined(name='foo') 597 >>> str(foo) 598 '' 599 >>> not foo 600 True 601 >>> foo + 42 602 Traceback (most recent call last): 603 ... 604 jinja2.exceptions.UndefinedError: 'foo' is undefined 605 """ 606 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', 607 '_undefined_exception') 608 609 def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): 610 self._undefined_hint = hint 611 self._undefined_obj = obj 612 self._undefined_name = name 613 self._undefined_exception = exc 614 615 @internalcode 616 def _fail_with_undefined_error(self, *args, **kwargs): 617 """Regular callback function for undefined objects that raises an 618 `jinja2.exceptions.UndefinedError` on call. 619 """ 620 if self._undefined_hint is None: 621 if self._undefined_obj is missing: 622 hint = '%r is undefined' % self._undefined_name 623 elif not isinstance(self._undefined_name, string_types): 624 hint = '%s has no element %r' % ( 625 object_type_repr(self._undefined_obj), 626 self._undefined_name 627 ) 628 else: 629 hint = '%r has no attribute %r' % ( 630 object_type_repr(self._undefined_obj), 631 self._undefined_name 632 ) 633 else: 634 hint = self._undefined_hint 635 raise self._undefined_exception(hint) 636 637 @internalcode 638 def __getattr__(self, name): 639 if name[:2] == '__': 640 raise AttributeError(name) 641 return self._fail_with_undefined_error() 642 643 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ 644 __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ 645 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ 646 __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ 647 __float__ = __complex__ = __pow__ = __rpow__ = __sub__ = \ 648 __rsub__ = _fail_with_undefined_error 649 650 def __eq__(self, other): 651 return type(self) is type(other) 652 653 def __ne__(self, other): 654 return not self.__eq__(other) 655 656 def __hash__(self): 657 return id(type(self)) 658 659 def __str__(self): 660 return u'' 661 662 def __len__(self): 663 return 0 664 665 def __iter__(self): 666 if 0: 667 yield None 668 669 def __nonzero__(self): 670 return False 671 __bool__ = __nonzero__ 672 673 def __repr__(self): 674 return 'Undefined' 675 676 677def make_logging_undefined(logger=None, base=None): 678 """Given a logger object this returns a new undefined class that will 679 log certain failures. It will log iterations and printing. If no 680 logger is given a default logger is created. 681 682 Example:: 683 684 logger = logging.getLogger(__name__) 685 LoggingUndefined = make_logging_undefined( 686 logger=logger, 687 base=Undefined 688 ) 689 690 .. versionadded:: 2.8 691 692 :param logger: the logger to use. If not provided, a default logger 693 is created. 694 :param base: the base class to add logging functionality to. This 695 defaults to :class:`Undefined`. 696 """ 697 if logger is None: 698 import logging 699 logger = logging.getLogger(__name__) 700 logger.addHandler(logging.StreamHandler(sys.stderr)) 701 if base is None: 702 base = Undefined 703 704 def _log_message(undef): 705 if undef._undefined_hint is None: 706 if undef._undefined_obj is missing: 707 hint = '%s is undefined' % undef._undefined_name 708 elif not isinstance(undef._undefined_name, string_types): 709 hint = '%s has no element %s' % ( 710 object_type_repr(undef._undefined_obj), 711 undef._undefined_name) 712 else: 713 hint = '%s has no attribute %s' % ( 714 object_type_repr(undef._undefined_obj), 715 undef._undefined_name) 716 else: 717 hint = undef._undefined_hint 718 logger.warning('Template variable warning: %s', hint) 719 720 class LoggingUndefined(base): 721 722 def _fail_with_undefined_error(self, *args, **kwargs): 723 try: 724 return base._fail_with_undefined_error(self, *args, **kwargs) 725 except self._undefined_exception as e: 726 logger.error('Template variable error: %s', str(e)) 727 raise e 728 729 def __str__(self): 730 rv = base.__str__(self) 731 _log_message(self) 732 return rv 733 734 def __iter__(self): 735 rv = base.__iter__(self) 736 _log_message(self) 737 return rv 738 739 if PY2: 740 def __nonzero__(self): 741 rv = base.__nonzero__(self) 742 _log_message(self) 743 return rv 744 745 def __unicode__(self): 746 rv = base.__unicode__(self) 747 _log_message(self) 748 return rv 749 else: 750 def __bool__(self): 751 rv = base.__bool__(self) 752 _log_message(self) 753 return rv 754 755 return LoggingUndefined 756 757 758@implements_to_string 759class DebugUndefined(Undefined): 760 """An undefined that returns the debug info when printed. 761 762 >>> foo = DebugUndefined(name='foo') 763 >>> str(foo) 764 '{{ foo }}' 765 >>> not foo 766 True 767 >>> foo + 42 768 Traceback (most recent call last): 769 ... 770 jinja2.exceptions.UndefinedError: 'foo' is undefined 771 """ 772 __slots__ = () 773 774 def __str__(self): 775 if self._undefined_hint is None: 776 if self._undefined_obj is missing: 777 return u'{{ %s }}' % self._undefined_name 778 return '{{ no such element: %s[%r] }}' % ( 779 object_type_repr(self._undefined_obj), 780 self._undefined_name 781 ) 782 return u'{{ undefined value printed: %s }}' % self._undefined_hint 783 784 785@implements_to_string 786class StrictUndefined(Undefined): 787 """An undefined that barks on print and iteration as well as boolean 788 tests and all kinds of comparisons. In other words: you can do nothing 789 with it except checking if it's defined using the `defined` test. 790 791 >>> foo = StrictUndefined(name='foo') 792 >>> str(foo) 793 Traceback (most recent call last): 794 ... 795 jinja2.exceptions.UndefinedError: 'foo' is undefined 796 >>> not foo 797 Traceback (most recent call last): 798 ... 799 jinja2.exceptions.UndefinedError: 'foo' is undefined 800 >>> foo + 42 801 Traceback (most recent call last): 802 ... 803 jinja2.exceptions.UndefinedError: 'foo' is undefined 804 """ 805 __slots__ = () 806 __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ 807 __ne__ = __bool__ = __hash__ = \ 808 Undefined._fail_with_undefined_error 809 810 811# remove remaining slots attributes, after the metaclass did the magic they 812# are unneeded and irritating as they contain wrong data for the subclasses. 813del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ 814