blob: 685a12da068c4808f2dfcec64d68e581f47e26fe [file] [log] [blame]
[email protected]88a0c562013-05-09 11:33:471# -*- coding: utf-8 -*-
2"""
3 jinja2.runtime
4 ~~~~~~~~~~~~~~
5
6 Runtime helpers.
7
8 :copyright: (c) 2010 by the Jinja Team.
9 :license: BSD.
10"""
yukishiinoa34bd6862016-09-08 04:27:0911import sys
12
[email protected]e6e05702013-09-06 09:48:3413from itertools import chain
[email protected]88a0c562013-05-09 11:33:4714from jinja2.nodes import EvalContext, _context_function_types
[email protected]e6e05702013-09-06 09:48:3415from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \
16 internalcode, object_type_repr
[email protected]88a0c562013-05-09 11:33:4717from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
18 TemplateNotFound
yukishiinoa34bd6862016-09-08 04:27:0919from jinja2._compat import imap, text_type, iteritems, \
[email protected]e6e05702013-09-06 09:48:3420 implements_iterator, implements_to_string, string_types, PY2
[email protected]88a0c562013-05-09 11:33:4721
22
23# these variables are exported to the template runtime
24__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
25 'TemplateRuntimeError', 'missing', 'concat', 'escape',
26 'markup_join', 'unicode_join', 'to_string', 'identity',
yukishiinoa34bd6862016-09-08 04:27:0927 'TemplateNotFound', 'make_logging_undefined']
[email protected]88a0c562013-05-09 11:33:4728
29#: the name of the function that is used to convert something into
[email protected]e6e05702013-09-06 09:48:3430#: a string. We can just use the text type here.
31to_string = text_type
[email protected]88a0c562013-05-09 11:33:4732
33#: the identity function. Useful for certain things in the environment
34identity = lambda x: x
35
[email protected]e6e05702013-09-06 09:48:3436_last_iteration = object()
37
[email protected]88a0c562013-05-09 11:33:4738
39def markup_join(seq):
40 """Concatenation that escapes if necessary and converts to unicode."""
41 buf = []
42 iterator = imap(soft_unicode, seq)
43 for arg in iterator:
44 buf.append(arg)
45 if hasattr(arg, '__html__'):
46 return Markup(u'').join(chain(buf, iterator))
47 return concat(buf)
48
49
50def unicode_join(seq):
51 """Simple args to unicode conversion and concatenation."""
[email protected]e6e05702013-09-06 09:48:3452 return concat(imap(text_type, seq))
[email protected]88a0c562013-05-09 11:33:4753
54
55def new_context(environment, template_name, blocks, vars=None,
56 shared=None, globals=None, locals=None):
57 """Internal helper to for context creation."""
58 if vars is None:
59 vars = {}
60 if shared:
61 parent = vars
62 else:
63 parent = dict(globals or (), **vars)
64 if locals:
65 # if the parent is shared a copy should be created because
66 # we don't want to modify the dict passed
67 if shared:
68 parent = dict(parent)
[email protected]e6e05702013-09-06 09:48:3469 for key, value in iteritems(locals):
[email protected]88a0c562013-05-09 11:33:4770 if key[:2] == 'l_' and value is not missing:
71 parent[key[2:]] = value
yukishiinoa34bd6862016-09-08 04:27:0972 return environment.context_class(environment, parent, template_name,
73 blocks)
[email protected]88a0c562013-05-09 11:33:4774
75
76class TemplateReference(object):
77 """The `self` in templates."""
78
79 def __init__(self, context):
80 self.__context = context
81
82 def __getitem__(self, name):
83 blocks = self.__context.blocks[name]
84 return BlockReference(name, self.__context, blocks, 0)
85
86 def __repr__(self):
87 return '<%s %r>' % (
88 self.__class__.__name__,
89 self.__context.name
90 )
91
92
93class Context(object):
94 """The template context holds the variables of a template. It stores the
95 values passed to the template and also the names the template exports.
96 Creating instances is neither supported nor useful as it's created
97 automatically at various stages of the template evaluation and should not
98 be created by hand.
99
100 The context is immutable. Modifications on :attr:`parent` **must not**
101 happen and modifications on :attr:`vars` are allowed from generated
102 template code only. Template filters and global functions marked as
103 :func:`contextfunction`\s get the active context passed as first argument
104 and are allowed to access the context read-only.
105
106 The template context supports read only dict operations (`get`,
107 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
108 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
109 method that doesn't fail with a `KeyError` but returns an
110 :class:`Undefined` object for missing variables.
111 """
112 __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
113 'name', 'blocks', '__weakref__')
114
115 def __init__(self, environment, parent, name, blocks):
116 self.parent = parent
117 self.vars = {}
118 self.environment = environment
119 self.eval_ctx = EvalContext(self.environment, name)
120 self.exported_vars = set()
121 self.name = name
122
123 # create the initial mapping of blocks. Whenever template inheritance
124 # takes place the runtime will update this mapping with the new blocks
125 # from the template.
[email protected]e6e05702013-09-06 09:48:34126 self.blocks = dict((k, [v]) for k, v in iteritems(blocks))
[email protected]88a0c562013-05-09 11:33:47127
128 def super(self, name, current):
129 """Render a parent block."""
130 try:
131 blocks = self.blocks[name]
132 index = blocks.index(current) + 1
133 blocks[index]
134 except LookupError:
135 return self.environment.undefined('there is no parent block '
136 'called %r.' % name,
137 name='super')
138 return BlockReference(name, self, blocks, index)
139
140 def get(self, key, default=None):
141 """Returns an item from the template context, if it doesn't exist
142 `default` is returned.
143 """
144 try:
145 return self[key]
146 except KeyError:
147 return default
148
149 def resolve(self, key):
150 """Looks up a variable like `__getitem__` or `get` but returns an
151 :class:`Undefined` object with the name of the name looked up.
152 """
153 if key in self.vars:
154 return self.vars[key]
155 if key in self.parent:
156 return self.parent[key]
157 return self.environment.undefined(name=key)
158
159 def get_exported(self):
160 """Get a new dict with the exported variables."""
161 return dict((k, self.vars[k]) for k in self.exported_vars)
162
163 def get_all(self):
164 """Return a copy of the complete context as dict including the
165 exported variables.
166 """
167 return dict(self.parent, **self.vars)
168
169 @internalcode
170 def call(__self, __obj, *args, **kwargs):
171 """Call the callable with the arguments and keyword arguments
172 provided but inject the active context or environment as first
173 argument if the callable is a :func:`contextfunction` or
174 :func:`environmentfunction`.
175 """
176 if __debug__:
yukishiinoa34bd6862016-09-08 04:27:09177 __traceback_hide__ = True # noqa
[email protected]e6e05702013-09-06 09:48:34178
179 # Allow callable classes to take a context
180 fn = __obj.__call__
181 for fn_type in ('contextfunction',
182 'evalcontextfunction',
183 'environmentfunction'):
184 if hasattr(fn, fn_type):
185 __obj = fn
186 break
187
[email protected]88a0c562013-05-09 11:33:47188 if isinstance(__obj, _context_function_types):
189 if getattr(__obj, 'contextfunction', 0):
190 args = (__self,) + args
191 elif getattr(__obj, 'evalcontextfunction', 0):
192 args = (__self.eval_ctx,) + args
193 elif getattr(__obj, 'environmentfunction', 0):
194 args = (__self.environment,) + args
195 try:
196 return __obj(*args, **kwargs)
197 except StopIteration:
198 return __self.environment.undefined('value was undefined because '
199 'a callable raised a '
200 'StopIteration exception')
201
202 def derived(self, locals=None):
203 """Internal helper function to create a derived context."""
204 context = new_context(self.environment, self.name, {},
205 self.parent, True, None, locals)
206 context.vars.update(self.vars)
207 context.eval_ctx = self.eval_ctx
[email protected]e6e05702013-09-06 09:48:34208 context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
[email protected]88a0c562013-05-09 11:33:47209 return context
210
211 def _all(meth):
212 proxy = lambda self: getattr(self.get_all(), meth)()
213 proxy.__doc__ = getattr(dict, meth).__doc__
214 proxy.__name__ = meth
215 return proxy
216
217 keys = _all('keys')
218 values = _all('values')
219 items = _all('items')
220
221 # not available on python 3
[email protected]e6e05702013-09-06 09:48:34222 if PY2:
[email protected]88a0c562013-05-09 11:33:47223 iterkeys = _all('iterkeys')
224 itervalues = _all('itervalues')
225 iteritems = _all('iteritems')
226 del _all
227
228 def __contains__(self, name):
229 return name in self.vars or name in self.parent
230
231 def __getitem__(self, key):
232 """Lookup a variable or raise `KeyError` if the variable is
233 undefined.
234 """
235 item = self.resolve(key)
236 if isinstance(item, Undefined):
237 raise KeyError(key)
238 return item
239
240 def __repr__(self):
241 return '<%s %s of %r>' % (
242 self.__class__.__name__,
243 repr(self.get_all()),
244 self.name
245 )
246
247
248# register the context as mapping if possible
249try:
250 from collections import Mapping
251 Mapping.register(Context)
252except ImportError:
253 pass
254
255
256class BlockReference(object):
257 """One block on a template reference."""
258
259 def __init__(self, name, context, stack, depth):
260 self.name = name
261 self._context = context
262 self._stack = stack
263 self._depth = depth
264
265 @property
266 def super(self):
267 """Super the block."""
268 if self._depth + 1 >= len(self._stack):
269 return self._context.environment. \
270 undefined('there is no parent block called %r.' %
271 self.name, name='super')
272 return BlockReference(self.name, self._context, self._stack,
273 self._depth + 1)
274
275 @internalcode
276 def __call__(self):
277 rv = concat(self._stack[self._depth](self._context))
278 if self._context.eval_ctx.autoescape:
279 rv = Markup(rv)
280 return rv
281
282
283class LoopContext(object):
284 """A loop context for dynamic iteration."""
285
[email protected]e6e05702013-09-06 09:48:34286 def __init__(self, iterable, recurse=None, depth0=0):
[email protected]88a0c562013-05-09 11:33:47287 self._iterator = iter(iterable)
288 self._recurse = recurse
[email protected]e6e05702013-09-06 09:48:34289 self._after = self._safe_next()
[email protected]88a0c562013-05-09 11:33:47290 self.index0 = -1
[email protected]e6e05702013-09-06 09:48:34291 self.depth0 = depth0
[email protected]88a0c562013-05-09 11:33:47292
293 # try to get the length of the iterable early. This must be done
294 # here because there are some broken iterators around where there
295 # __len__ is the number of iterations left (i'm looking at your
296 # listreverseiterator!).
297 try:
298 self._length = len(iterable)
299 except (TypeError, AttributeError):
300 self._length = None
301
302 def cycle(self, *args):
303 """Cycles among the arguments with the current loop index."""
304 if not args:
305 raise TypeError('no items for cycling given')
306 return args[self.index0 % len(args)]
307
308 first = property(lambda x: x.index0 == 0)
[email protected]e6e05702013-09-06 09:48:34309 last = property(lambda x: x._after is _last_iteration)
[email protected]88a0c562013-05-09 11:33:47310 index = property(lambda x: x.index0 + 1)
311 revindex = property(lambda x: x.length - x.index0)
312 revindex0 = property(lambda x: x.length - x.index)
[email protected]e6e05702013-09-06 09:48:34313 depth = property(lambda x: x.depth0 + 1)
[email protected]88a0c562013-05-09 11:33:47314
315 def __len__(self):
316 return self.length
317
318 def __iter__(self):
319 return LoopContextIterator(self)
320
[email protected]e6e05702013-09-06 09:48:34321 def _safe_next(self):
322 try:
323 return next(self._iterator)
324 except StopIteration:
325 return _last_iteration
326
[email protected]88a0c562013-05-09 11:33:47327 @internalcode
328 def loop(self, iterable):
329 if self._recurse is None:
330 raise TypeError('Tried to call non recursive loop. Maybe you '
331 "forgot the 'recursive' modifier.")
[email protected]e6e05702013-09-06 09:48:34332 return self._recurse(iterable, self._recurse, self.depth0 + 1)
[email protected]88a0c562013-05-09 11:33:47333
334 # a nifty trick to enhance the error message if someone tried to call
335 # the the loop without or with too many arguments.
336 __call__ = loop
337 del loop
338
339 @property
340 def length(self):
341 if self._length is None:
342 # if was not possible to get the length of the iterator when
343 # the loop context was created (ie: iterating over a generator)
344 # we have to convert the iterable into a sequence and use the
yukishiinoa34bd6862016-09-08 04:27:09345 # length of that + the number of iterations so far.
[email protected]88a0c562013-05-09 11:33:47346 iterable = tuple(self._iterator)
347 self._iterator = iter(iterable)
yukishiinoa34bd6862016-09-08 04:27:09348 iterations_done = self.index0 + 2
349 self._length = len(iterable) + iterations_done
[email protected]88a0c562013-05-09 11:33:47350 return self._length
351
352 def __repr__(self):
353 return '<%s %r/%r>' % (
354 self.__class__.__name__,
355 self.index,
356 self.length
357 )
358
359
[email protected]e6e05702013-09-06 09:48:34360@implements_iterator
[email protected]88a0c562013-05-09 11:33:47361class LoopContextIterator(object):
362 """The iterator for a loop context."""
363 __slots__ = ('context',)
364
365 def __init__(self, context):
366 self.context = context
367
368 def __iter__(self):
369 return self
370
[email protected]e6e05702013-09-06 09:48:34371 def __next__(self):
[email protected]88a0c562013-05-09 11:33:47372 ctx = self.context
373 ctx.index0 += 1
[email protected]e6e05702013-09-06 09:48:34374 if ctx._after is _last_iteration:
375 raise StopIteration()
376 next_elem = ctx._after
377 ctx._after = ctx._safe_next()
378 return next_elem, ctx
[email protected]88a0c562013-05-09 11:33:47379
380
381class Macro(object):
382 """Wraps a macro function."""
383
384 def __init__(self, environment, func, name, arguments, defaults,
385 catch_kwargs, catch_varargs, caller):
386 self._environment = environment
387 self._func = func
388 self._argument_count = len(arguments)
389 self.name = name
390 self.arguments = arguments
391 self.defaults = defaults
392 self.catch_kwargs = catch_kwargs
393 self.catch_varargs = catch_varargs
394 self.caller = caller
395
396 @internalcode
397 def __call__(self, *args, **kwargs):
398 # try to consume the positional arguments
399 arguments = list(args[:self._argument_count])
400 off = len(arguments)
401
402 # if the number of arguments consumed is not the number of
403 # arguments expected we start filling in keyword arguments
404 # and defaults.
405 if off != self._argument_count:
406 for idx, name in enumerate(self.arguments[len(arguments):]):
407 try:
408 value = kwargs.pop(name)
409 except KeyError:
410 try:
411 value = self.defaults[idx - self._argument_count + off]
412 except IndexError:
413 value = self._environment.undefined(
414 'parameter %r was not provided' % name, name=name)
415 arguments.append(value)
416
417 # it's important that the order of these arguments does not change
418 # if not also changed in the compiler's `function_scoping` method.
419 # the order is caller, keyword arguments, positional arguments!
420 if self.caller:
421 caller = kwargs.pop('caller', None)
422 if caller is None:
423 caller = self._environment.undefined('No caller defined',
424 name='caller')
425 arguments.append(caller)
426 if self.catch_kwargs:
427 arguments.append(kwargs)
428 elif kwargs:
429 raise TypeError('macro %r takes no keyword argument %r' %
430 (self.name, next(iter(kwargs))))
431 if self.catch_varargs:
432 arguments.append(args[self._argument_count:])
433 elif len(args) > self._argument_count:
434 raise TypeError('macro %r takes not more than %d argument(s)' %
435 (self.name, len(self.arguments)))
436 return self._func(*arguments)
437
438 def __repr__(self):
439 return '<%s %s>' % (
440 self.__class__.__name__,
441 self.name is None and 'anonymous' or repr(self.name)
442 )
443
444
[email protected]e6e05702013-09-06 09:48:34445@implements_to_string
[email protected]88a0c562013-05-09 11:33:47446class Undefined(object):
447 """The default undefined type. This undefined type can be printed and
yukishiinoa34bd6862016-09-08 04:27:09448 iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`:
[email protected]88a0c562013-05-09 11:33:47449
450 >>> foo = Undefined(name='foo')
451 >>> str(foo)
452 ''
453 >>> not foo
454 True
455 >>> foo + 42
456 Traceback (most recent call last):
457 ...
yukishiinoa34bd6862016-09-08 04:27:09458 jinja2.exceptions.UndefinedError: 'foo' is undefined
[email protected]88a0c562013-05-09 11:33:47459 """
460 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
461 '_undefined_exception')
462
463 def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
464 self._undefined_hint = hint
465 self._undefined_obj = obj
466 self._undefined_name = name
467 self._undefined_exception = exc
468
469 @internalcode
470 def _fail_with_undefined_error(self, *args, **kwargs):
471 """Regular callback function for undefined objects that raises an
yukishiinoa34bd6862016-09-08 04:27:09472 `jinja2.exceptions.UndefinedError` on call.
[email protected]88a0c562013-05-09 11:33:47473 """
474 if self._undefined_hint is None:
475 if self._undefined_obj is missing:
476 hint = '%r is undefined' % self._undefined_name
[email protected]e6e05702013-09-06 09:48:34477 elif not isinstance(self._undefined_name, string_types):
[email protected]88a0c562013-05-09 11:33:47478 hint = '%s has no element %r' % (
479 object_type_repr(self._undefined_obj),
480 self._undefined_name
481 )
482 else:
483 hint = '%r has no attribute %r' % (
484 object_type_repr(self._undefined_obj),
485 self._undefined_name
486 )
487 else:
488 hint = self._undefined_hint
489 raise self._undefined_exception(hint)
490
491 @internalcode
492 def __getattr__(self, name):
493 if name[:2] == '__':
494 raise AttributeError(name)
495 return self._fail_with_undefined_error()
496
497 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
yukishiinoa34bd6862016-09-08 04:27:09498 __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
499 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
500 __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \
501 __float__ = __complex__ = __pow__ = __rpow__ = \
[email protected]88a0c562013-05-09 11:33:47502 _fail_with_undefined_error
503
[email protected]e6e05702013-09-06 09:48:34504 def __eq__(self, other):
505 return type(self) is type(other)
[email protected]88a0c562013-05-09 11:33:47506
[email protected]e6e05702013-09-06 09:48:34507 def __ne__(self, other):
508 return not self.__eq__(other)
509
510 def __hash__(self):
511 return id(type(self))
512
513 def __str__(self):
[email protected]88a0c562013-05-09 11:33:47514 return u''
515
516 def __len__(self):
517 return 0
518
519 def __iter__(self):
520 if 0:
521 yield None
522
523 def __nonzero__(self):
524 return False
yukishiinoa34bd6862016-09-08 04:27:09525 __bool__ = __nonzero__
[email protected]88a0c562013-05-09 11:33:47526
527 def __repr__(self):
528 return 'Undefined'
529
530
yukishiinoa34bd6862016-09-08 04:27:09531def make_logging_undefined(logger=None, base=None):
532 """Given a logger object this returns a new undefined class that will
533 log certain failures. It will log iterations and printing. If no
534 logger is given a default logger is created.
535
536 Example::
537
538 logger = logging.getLogger(__name__)
539 LoggingUndefined = make_logging_undefined(
540 logger=logger,
541 base=Undefined
542 )
543
544 .. versionadded:: 2.8
545
546 :param logger: the logger to use. If not provided, a default logger
547 is created.
548 :param base: the base class to add logging functionality to. This
549 defaults to :class:`Undefined`.
550 """
551 if logger is None:
552 import logging
553 logger = logging.getLogger(__name__)
554 logger.addHandler(logging.StreamHandler(sys.stderr))
555 if base is None:
556 base = Undefined
557
558 def _log_message(undef):
559 if undef._undefined_hint is None:
560 if undef._undefined_obj is missing:
561 hint = '%s is undefined' % undef._undefined_name
562 elif not isinstance(undef._undefined_name, string_types):
563 hint = '%s has no element %s' % (
564 object_type_repr(undef._undefined_obj),
565 undef._undefined_name)
566 else:
567 hint = '%s has no attribute %s' % (
568 object_type_repr(undef._undefined_obj),
569 undef._undefined_name)
570 else:
571 hint = undef._undefined_hint
572 logger.warning('Template variable warning: %s', hint)
573
574 class LoggingUndefined(base):
575
576 def _fail_with_undefined_error(self, *args, **kwargs):
577 try:
578 return base._fail_with_undefined_error(self, *args, **kwargs)
579 except self._undefined_exception as e:
580 logger.error('Template variable error: %s', str(e))
581 raise e
582
583 def __str__(self):
584 rv = base.__str__(self)
585 _log_message(self)
586 return rv
587
588 def __iter__(self):
589 rv = base.__iter__(self)
590 _log_message(self)
591 return rv
592
593 if PY2:
594 def __nonzero__(self):
595 rv = base.__nonzero__(self)
596 _log_message(self)
597 return rv
598
599 def __unicode__(self):
600 rv = base.__unicode__(self)
601 _log_message(self)
602 return rv
603 else:
604 def __bool__(self):
605 rv = base.__bool__(self)
606 _log_message(self)
607 return rv
608
609 return LoggingUndefined
610
611
[email protected]e6e05702013-09-06 09:48:34612@implements_to_string
[email protected]88a0c562013-05-09 11:33:47613class DebugUndefined(Undefined):
614 """An undefined that returns the debug info when printed.
615
616 >>> foo = DebugUndefined(name='foo')
617 >>> str(foo)
618 '{{ foo }}'
619 >>> not foo
620 True
621 >>> foo + 42
622 Traceback (most recent call last):
623 ...
yukishiinoa34bd6862016-09-08 04:27:09624 jinja2.exceptions.UndefinedError: 'foo' is undefined
[email protected]88a0c562013-05-09 11:33:47625 """
626 __slots__ = ()
627
[email protected]e6e05702013-09-06 09:48:34628 def __str__(self):
[email protected]88a0c562013-05-09 11:33:47629 if self._undefined_hint is None:
630 if self._undefined_obj is missing:
631 return u'{{ %s }}' % self._undefined_name
632 return '{{ no such element: %s[%r] }}' % (
633 object_type_repr(self._undefined_obj),
634 self._undefined_name
635 )
636 return u'{{ undefined value printed: %s }}' % self._undefined_hint
637
638
[email protected]e6e05702013-09-06 09:48:34639@implements_to_string
[email protected]88a0c562013-05-09 11:33:47640class StrictUndefined(Undefined):
641 """An undefined that barks on print and iteration as well as boolean
642 tests and all kinds of comparisons. In other words: you can do nothing
643 with it except checking if it's defined using the `defined` test.
644
645 >>> foo = StrictUndefined(name='foo')
646 >>> str(foo)
647 Traceback (most recent call last):
648 ...
yukishiinoa34bd6862016-09-08 04:27:09649 jinja2.exceptions.UndefinedError: 'foo' is undefined
[email protected]88a0c562013-05-09 11:33:47650 >>> not foo
651 Traceback (most recent call last):
652 ...
yukishiinoa34bd6862016-09-08 04:27:09653 jinja2.exceptions.UndefinedError: 'foo' is undefined
[email protected]88a0c562013-05-09 11:33:47654 >>> foo + 42
655 Traceback (most recent call last):
656 ...
yukishiinoa34bd6862016-09-08 04:27:09657 jinja2.exceptions.UndefinedError: 'foo' is undefined
[email protected]88a0c562013-05-09 11:33:47658 """
659 __slots__ = ()
[email protected]e6e05702013-09-06 09:48:34660 __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \
661 __ne__ = __bool__ = __hash__ = \
662 Undefined._fail_with_undefined_error
[email protected]88a0c562013-05-09 11:33:47663
664
665# remove remaining slots attributes, after the metaclass did the magic they
666# are unneeded and irritating as they contain wrong data for the subclasses.
667del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__