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, abc 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 316abc.Mapping.register(Context) 317 318 319class BlockReference(object): 320 """One block on a template reference.""" 321 322 def __init__(self, name, context, stack, depth): 323 self.name = name 324 self._context = context 325 self._stack = stack 326 self._depth = depth 327 328 @property 329 def super(self): 330 """Super the block.""" 331 if self._depth + 1 >= len(self._stack): 332 return self._context.environment. \ 333 undefined('there is no parent block called %r.' % 334 self.name, name='super') 335 return BlockReference(self.name, self._context, self._stack, 336 self._depth + 1) 337 338 @internalcode 339 def __call__(self): 340 rv = concat(self._stack[self._depth](self._context)) 341 if self._context.eval_ctx.autoescape: 342 rv = Markup(rv) 343 return rv 344 345 346class LoopContextBase(object): 347 """A loop context for dynamic iteration.""" 348 349 _before = _first_iteration 350 _current = _first_iteration 351 _after = _last_iteration 352 _length = None 353 354 def __init__(self, undefined, recurse=None, depth0=0): 355 self._undefined = undefined 356 self._recurse = recurse 357 self.index0 = -1 358 self.depth0 = depth0 359 self._last_checked_value = missing 360 361 def cycle(self, *args): 362 """Cycles among the arguments with the current loop index.""" 363 if not args: 364 raise TypeError('no items for cycling given') 365 return args[self.index0 % len(args)] 366 367 def changed(self, *value): 368 """Checks whether the value has changed since the last call.""" 369 if self._last_checked_value != value: 370 self._last_checked_value = value 371 return True 372 return False 373 374 first = property(lambda x: x.index0 == 0) 375 last = property(lambda x: x._after is _last_iteration) 376 index = property(lambda x: x.index0 + 1) 377 revindex = property(lambda x: x.length - x.index0) 378 revindex0 = property(lambda x: x.length - x.index) 379 depth = property(lambda x: x.depth0 + 1) 380 381 @property 382 def previtem(self): 383 if self._before is _first_iteration: 384 return self._undefined('there is no previous item') 385 return self._before 386 387 @property 388 def nextitem(self): 389 if self._after is _last_iteration: 390 return self._undefined('there is no next item') 391 return self._after 392 393 def __len__(self): 394 return self.length 395 396 @internalcode 397 def loop(self, iterable): 398 if self._recurse is None: 399 raise TypeError('Tried to call non recursive loop. Maybe you ' 400 "forgot the 'recursive' modifier.") 401 return self._recurse(iterable, self._recurse, self.depth0 + 1) 402 403 # a nifty trick to enhance the error message if someone tried to call 404 # the the loop without or with too many arguments. 405 __call__ = loop 406 del loop 407 408 def __repr__(self): 409 return '<%s %r/%r>' % ( 410 self.__class__.__name__, 411 self.index, 412 self.length 413 ) 414 415 416class LoopContext(LoopContextBase): 417 418 def __init__(self, iterable, undefined, recurse=None, depth0=0): 419 LoopContextBase.__init__(self, undefined, recurse, depth0) 420 self._iterator = iter(iterable) 421 422 # try to get the length of the iterable early. This must be done 423 # here because there are some broken iterators around where there 424 # __len__ is the number of iterations left (i'm looking at your 425 # listreverseiterator!). 426 try: 427 self._length = len(iterable) 428 except (TypeError, AttributeError): 429 self._length = None 430 self._after = self._safe_next() 431 432 @property 433 def length(self): 434 if self._length is None: 435 # if was not possible to get the length of the iterator when 436 # the loop context was created (ie: iterating over a generator) 437 # we have to convert the iterable into a sequence and use the 438 # length of that + the number of iterations so far. 439 iterable = tuple(self._iterator) 440 self._iterator = iter(iterable) 441 iterations_done = self.index0 + 2 442 self._length = len(iterable) + iterations_done 443 return self._length 444 445 def __iter__(self): 446 return LoopContextIterator(self) 447 448 def _safe_next(self): 449 try: 450 return next(self._iterator) 451 except StopIteration: 452 return _last_iteration 453 454 455@implements_iterator 456class LoopContextIterator(object): 457 """The iterator for a loop context.""" 458 __slots__ = ('context',) 459 460 def __init__(self, context): 461 self.context = context 462 463 def __iter__(self): 464 return self 465 466 def __next__(self): 467 ctx = self.context 468 ctx.index0 += 1 469 if ctx._after is _last_iteration: 470 raise StopIteration() 471 ctx._before = ctx._current 472 ctx._current = ctx._after 473 ctx._after = ctx._safe_next() 474 return ctx._current, ctx 475 476 477class Macro(object): 478 """Wraps a macro function.""" 479 480 def __init__(self, environment, func, name, arguments, 481 catch_kwargs, catch_varargs, caller, 482 default_autoescape=None): 483 self._environment = environment 484 self._func = func 485 self._argument_count = len(arguments) 486 self.name = name 487 self.arguments = arguments 488 self.catch_kwargs = catch_kwargs 489 self.catch_varargs = catch_varargs 490 self.caller = caller 491 self.explicit_caller = 'caller' in arguments 492 if default_autoescape is None: 493 default_autoescape = environment.autoescape 494 self._default_autoescape = default_autoescape 495 496 @internalcode 497 @evalcontextfunction 498 def __call__(self, *args, **kwargs): 499 # This requires a bit of explanation, In the past we used to 500 # decide largely based on compile-time information if a macro is 501 # safe or unsafe. While there was a volatile mode it was largely 502 # unused for deciding on escaping. This turns out to be 503 # problemtic for macros because if a macro is safe or not not so 504 # much depends on the escape mode when it was defined but when it 505 # was used. 506 # 507 # Because however we export macros from the module system and 508 # there are historic callers that do not pass an eval context (and 509 # will continue to not pass one), we need to perform an instance 510 # check here. 511 # 512 # This is considered safe because an eval context is not a valid 513 # argument to callables otherwise anwyays. Worst case here is 514 # that if no eval context is passed we fall back to the compile 515 # time autoescape flag. 516 if args and isinstance(args[0], EvalContext): 517 autoescape = args[0].autoescape 518 args = args[1:] 519 else: 520 autoescape = self._default_autoescape 521 522 # try to consume the positional arguments 523 arguments = list(args[:self._argument_count]) 524 off = len(arguments) 525 526 # For information why this is necessary refer to the handling 527 # of caller in the `macro_body` handler in the compiler. 528 found_caller = False 529 530 # if the number of arguments consumed is not the number of 531 # arguments expected we start filling in keyword arguments 532 # and defaults. 533 if off != self._argument_count: 534 for idx, name in enumerate(self.arguments[len(arguments):]): 535 try: 536 value = kwargs.pop(name) 537 except KeyError: 538 value = missing 539 if name == 'caller': 540 found_caller = True 541 arguments.append(value) 542 else: 543 found_caller = self.explicit_caller 544 545 # it's important that the order of these arguments does not change 546 # if not also changed in the compiler's `function_scoping` method. 547 # the order is caller, keyword arguments, positional arguments! 548 if self.caller and not found_caller: 549 caller = kwargs.pop('caller', None) 550 if caller is None: 551 caller = self._environment.undefined('No caller defined', 552 name='caller') 553 arguments.append(caller) 554 555 if self.catch_kwargs: 556 arguments.append(kwargs) 557 elif kwargs: 558 if 'caller' in kwargs: 559 raise TypeError('macro %r was invoked with two values for ' 560 'the special caller argument. This is ' 561 'most likely a bug.' % self.name) 562 raise TypeError('macro %r takes no keyword argument %r' % 563 (self.name, next(iter(kwargs)))) 564 if self.catch_varargs: 565 arguments.append(args[self._argument_count:]) 566 elif len(args) > self._argument_count: 567 raise TypeError('macro %r takes not more than %d argument(s)' % 568 (self.name, len(self.arguments))) 569 570 return self._invoke(arguments, autoescape) 571 572 def _invoke(self, arguments, autoescape): 573 """This method is being swapped out by the async implementation.""" 574 rv = self._func(*arguments) 575 if autoescape: 576 rv = Markup(rv) 577 return rv 578 579 def __repr__(self): 580 return '<%s %s>' % ( 581 self.__class__.__name__, 582 self.name is None and 'anonymous' or repr(self.name) 583 ) 584 585 586@implements_to_string 587class Undefined(object): 588 """The default undefined type. This undefined type can be printed and 589 iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`: 590 591 >>> foo = Undefined(name='foo') 592 >>> str(foo) 593 '' 594 >>> not foo 595 True 596 >>> foo + 42 597 Traceback (most recent call last): 598 ... 599 jinja2.exceptions.UndefinedError: 'foo' is undefined 600 """ 601 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', 602 '_undefined_exception') 603 604 def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): 605 self._undefined_hint = hint 606 self._undefined_obj = obj 607 self._undefined_name = name 608 self._undefined_exception = exc 609 610 @internalcode 611 def _fail_with_undefined_error(self, *args, **kwargs): 612 """Regular callback function for undefined objects that raises an 613 `jinja2.exceptions.UndefinedError` on call. 614 """ 615 if self._undefined_hint is None: 616 if self._undefined_obj is missing: 617 hint = '%r is undefined' % self._undefined_name 618 elif not isinstance(self._undefined_name, string_types): 619 hint = '%s has no element %r' % ( 620 object_type_repr(self._undefined_obj), 621 self._undefined_name 622 ) 623 else: 624 hint = '%r has no attribute %r' % ( 625 object_type_repr(self._undefined_obj), 626 self._undefined_name 627 ) 628 else: 629 hint = self._undefined_hint 630 raise self._undefined_exception(hint) 631 632 @internalcode 633 def __getattr__(self, name): 634 if name[:2] == '__': 635 raise AttributeError(name) 636 return self._fail_with_undefined_error() 637 638 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ 639 __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ 640 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ 641 __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ 642 __float__ = __complex__ = __pow__ = __rpow__ = __sub__ = \ 643 __rsub__ = _fail_with_undefined_error 644 645 def __eq__(self, other): 646 return type(self) is type(other) 647 648 def __ne__(self, other): 649 return not self.__eq__(other) 650 651 def __hash__(self): 652 return id(type(self)) 653 654 def __str__(self): 655 return u'' 656 657 def __len__(self): 658 return 0 659 660 def __iter__(self): 661 if 0: 662 yield None 663 664 def __nonzero__(self): 665 return False 666 __bool__ = __nonzero__ 667 668 def __repr__(self): 669 return 'Undefined' 670 671 672def make_logging_undefined(logger=None, base=None): 673 """Given a logger object this returns a new undefined class that will 674 log certain failures. It will log iterations and printing. If no 675 logger is given a default logger is created. 676 677 Example:: 678 679 logger = logging.getLogger(__name__) 680 LoggingUndefined = make_logging_undefined( 681 logger=logger, 682 base=Undefined 683 ) 684 685 .. versionadded:: 2.8 686 687 :param logger: the logger to use. If not provided, a default logger 688 is created. 689 :param base: the base class to add logging functionality to. This 690 defaults to :class:`Undefined`. 691 """ 692 if logger is None: 693 import logging 694 logger = logging.getLogger(__name__) 695 logger.addHandler(logging.StreamHandler(sys.stderr)) 696 if base is None: 697 base = Undefined 698 699 def _log_message(undef): 700 if undef._undefined_hint is None: 701 if undef._undefined_obj is missing: 702 hint = '%s is undefined' % undef._undefined_name 703 elif not isinstance(undef._undefined_name, string_types): 704 hint = '%s has no element %s' % ( 705 object_type_repr(undef._undefined_obj), 706 undef._undefined_name) 707 else: 708 hint = '%s has no attribute %s' % ( 709 object_type_repr(undef._undefined_obj), 710 undef._undefined_name) 711 else: 712 hint = undef._undefined_hint 713 logger.warning('Template variable warning: %s', hint) 714 715 class LoggingUndefined(base): 716 717 def _fail_with_undefined_error(self, *args, **kwargs): 718 try: 719 return base._fail_with_undefined_error(self, *args, **kwargs) 720 except self._undefined_exception as e: 721 logger.error('Template variable error: %s', str(e)) 722 raise e 723 724 def __str__(self): 725 rv = base.__str__(self) 726 _log_message(self) 727 return rv 728 729 def __iter__(self): 730 rv = base.__iter__(self) 731 _log_message(self) 732 return rv 733 734 if PY2: 735 def __nonzero__(self): 736 rv = base.__nonzero__(self) 737 _log_message(self) 738 return rv 739 740 def __unicode__(self): 741 rv = base.__unicode__(self) 742 _log_message(self) 743 return rv 744 else: 745 def __bool__(self): 746 rv = base.__bool__(self) 747 _log_message(self) 748 return rv 749 750 return LoggingUndefined 751 752 753@implements_to_string 754class DebugUndefined(Undefined): 755 """An undefined that returns the debug info when printed. 756 757 >>> foo = DebugUndefined(name='foo') 758 >>> str(foo) 759 '{{ foo }}' 760 >>> not foo 761 True 762 >>> foo + 42 763 Traceback (most recent call last): 764 ... 765 jinja2.exceptions.UndefinedError: 'foo' is undefined 766 """ 767 __slots__ = () 768 769 def __str__(self): 770 if self._undefined_hint is None: 771 if self._undefined_obj is missing: 772 return u'{{ %s }}' % self._undefined_name 773 return '{{ no such element: %s[%r] }}' % ( 774 object_type_repr(self._undefined_obj), 775 self._undefined_name 776 ) 777 return u'{{ undefined value printed: %s }}' % self._undefined_hint 778 779 780@implements_to_string 781class StrictUndefined(Undefined): 782 """An undefined that barks on print and iteration as well as boolean 783 tests and all kinds of comparisons. In other words: you can do nothing 784 with it except checking if it's defined using the `defined` test. 785 786 >>> foo = StrictUndefined(name='foo') 787 >>> str(foo) 788 Traceback (most recent call last): 789 ... 790 jinja2.exceptions.UndefinedError: 'foo' is undefined 791 >>> not foo 792 Traceback (most recent call last): 793 ... 794 jinja2.exceptions.UndefinedError: 'foo' is undefined 795 >>> foo + 42 796 Traceback (most recent call last): 797 ... 798 jinja2.exceptions.UndefinedError: 'foo' is undefined 799 """ 800 __slots__ = () 801 __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ 802 __ne__ = __bool__ = __hash__ = \ 803 Undefined._fail_with_undefined_error 804 805 806# remove remaining slots attributes, after the metaclass did the magic they 807# are unneeded and irritating as they contain wrong data for the subclasses. 808del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ 809