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