blob: d6c555357495e946588ad49a68ecff73dd29815d [file] [log] [blame]
Armin Ronachere791c2a2008-04-07 18:39:54 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.runtime
4 ~~~~~~~~~~~~~~
5
6 Runtime helpers.
7
8 :copyright: Copyright 2008 by Armin Ronacher.
9 :license: GNU GPL.
10"""
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020011import sys
Armin Ronacher24b65582008-05-26 13:35:58 +020012from types import FunctionType, MethodType
Armin Ronacherd1342312008-04-28 12:20:12 +020013from itertools import chain, imap
Armin Ronacher7ceced52008-05-03 10:15:31 +020014from jinja2.utils import Markup, partial, soft_unicode, escape, missing, concat
Armin Ronacherd1342312008-04-28 12:20:12 +020015from jinja2.exceptions import UndefinedError, TemplateRuntimeError
Armin Ronachere791c2a2008-04-07 18:39:54 +020016
17
Armin Ronacher2feed1d2008-04-26 16:26:52 +020018# these variables are exported to the template runtime
Armin Ronacher19cf9c22008-05-01 12:49:53 +020019__all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
20 'TemplateRuntimeError', 'missing', 'concat', 'escape',
Armin Ronacherd1342312008-04-28 12:20:12 +020021 'markup_join', 'unicode_join']
Armin Ronacher0611e492008-04-25 23:44:14 +020022
Armin Ronacher24b65582008-05-26 13:35:58 +020023_context_function_types = (FunctionType, MethodType)
24
Armin Ronacher0611e492008-04-25 23:44:14 +020025
Armin Ronacherdc02b642008-05-15 22:47:27 +020026def markup_join(seq):
Armin Ronacherd1342312008-04-28 12:20:12 +020027 """Concatenation that escapes if necessary and converts to unicode."""
28 buf = []
Armin Ronacherdc02b642008-05-15 22:47:27 +020029 iterator = imap(soft_unicode, seq)
Armin Ronacherd1342312008-04-28 12:20:12 +020030 for arg in iterator:
31 buf.append(arg)
32 if hasattr(arg, '__html__'):
33 return Markup(u'').join(chain(buf, iterator))
34 return concat(buf)
35
36
Armin Ronacherdc02b642008-05-15 22:47:27 +020037def unicode_join(seq):
Armin Ronacherd1342312008-04-28 12:20:12 +020038 """Simple args to unicode conversion and concatenation."""
Armin Ronacherdc02b642008-05-15 22:47:27 +020039 return concat(imap(unicode, seq))
Armin Ronacherd1342312008-04-28 12:20:12 +020040
41
Armin Ronacher19cf9c22008-05-01 12:49:53 +020042class Context(object):
Armin Ronacher7259c762008-04-30 13:03:59 +020043 """The template context holds the variables of a template. It stores the
44 values passed to the template and also the names the template exports.
45 Creating instances is neither supported nor useful as it's created
46 automatically at various stages of the template evaluation and should not
47 be created by hand.
Armin Ronacherc9705c22008-04-27 21:28:03 +020048
Armin Ronacher7259c762008-04-30 13:03:59 +020049 The context is immutable. Modifications on :attr:`parent` **must not**
50 happen and modifications on :attr:`vars` are allowed from generated
51 template code only. Template filters and global functions marked as
52 :func:`contextfunction`\s get the active context passed as first argument
53 and are allowed to access the context read-only.
54
55 The template context supports read only dict operations (`get`,
Armin Ronacherf35e2812008-05-06 16:04:10 +020056 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
57 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
58 method that doesn't fail with a `KeyError` but returns an
59 :class:`Undefined` object for missing variables.
Armin Ronacher9706fab2008-04-08 18:49:56 +020060 """
Armin Ronacher771c7502008-05-18 23:14:14 +020061 __slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
62 'blocks')
Armin Ronachere791c2a2008-04-07 18:39:54 +020063
Armin Ronacher203bfcb2008-04-24 21:54:44 +020064 def __init__(self, environment, parent, name, blocks):
65 self.parent = parent
Armin Ronacher2feed1d2008-04-26 16:26:52 +020066 self.vars = vars = {}
Armin Ronacherc63243e2008-04-14 22:53:58 +020067 self.environment = environment
Armin Ronacher203bfcb2008-04-24 21:54:44 +020068 self.exported_vars = set()
Armin Ronacher68f77672008-04-17 11:50:39 +020069 self.name = name
Armin Ronacher203bfcb2008-04-24 21:54:44 +020070
Armin Ronacher203bfcb2008-04-24 21:54:44 +020071 # create the initial mapping of blocks. Whenever template inheritance
72 # takes place the runtime will update this mapping with the new blocks
73 # from the template.
Armin Ronacher75cfb862008-04-11 13:47:22 +020074 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
Armin Ronacherf059ec12008-04-11 22:21:00 +020075
Armin Ronacher203bfcb2008-04-24 21:54:44 +020076 def super(self, name, current):
Armin Ronacher62f8a292008-04-13 23:18:05 +020077 """Render a parent block."""
Armin Ronacherc9705c22008-04-27 21:28:03 +020078 try:
79 blocks = self.blocks[name]
Armin Ronacher83fbc0f2008-05-15 12:22:28 +020080 block = blocks[blocks.index(current) + 1]
Armin Ronacherc9705c22008-04-27 21:28:03 +020081 except LookupError:
Armin Ronacher9a822052008-04-17 18:44:07 +020082 return self.environment.undefined('there is no parent block '
Armin Ronacher19cf9c22008-05-01 12:49:53 +020083 'called %r.' % name,
84 name='super')
Armin Ronacherd1342312008-04-28 12:20:12 +020085 wrap = self.environment.autoescape and Markup or (lambda x: x)
Armin Ronacher83fbc0f2008-05-15 12:22:28 +020086 render = lambda: wrap(concat(block(self)))
Armin Ronacherc9705c22008-04-27 21:28:03 +020087 render.__name__ = render.name = name
88 return render
Armin Ronachere791c2a2008-04-07 18:39:54 +020089
Armin Ronacher53042292008-04-26 18:30:19 +020090 def get(self, key, default=None):
Armin Ronacher7259c762008-04-30 13:03:59 +020091 """Returns an item from the template context, if it doesn't exist
92 `default` is returned.
93 """
Armin Ronacherf35e2812008-05-06 16:04:10 +020094 try:
95 return self[key]
96 except KeyError:
97 return default
98
99 def resolve(self, key):
100 """Looks up a variable like `__getitem__` or `get` but returns an
101 :class:`Undefined` object with the name of the name looked up.
102 """
Armin Ronacher53042292008-04-26 18:30:19 +0200103 if key in self.vars:
104 return self.vars[key]
105 if key in self.parent:
106 return self.parent[key]
Armin Ronacherf35e2812008-05-06 16:04:10 +0200107 return self.environment.undefined(name=key)
Armin Ronacherb5124e62008-04-25 00:36:14 +0200108
Armin Ronacher9706fab2008-04-08 18:49:56 +0200109 def get_exported(self):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200110 """Get a new dict with the exported variables."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200111 return dict((k, self.vars[k]) for k in self.exported_vars)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200112
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200113 def get_all(self):
Armin Ronacher7259c762008-04-30 13:03:59 +0200114 """Return a copy of the complete context as dict including the
Armin Ronacher5411ce72008-05-25 11:36:22 +0200115 exported variables.
Armin Ronacher7259c762008-04-30 13:03:59 +0200116 """
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200117 return dict(self.parent, **self.vars)
118
Armin Ronacherfd310492008-05-25 00:16:51 +0200119 def call(__self, __obj, *args, **kwargs):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200120 """Call the callable with the arguments and keyword arguments
121 provided but inject the active context or environment as first
122 argument if the callable is a :func:`contextfunction` or
123 :func:`environmentfunction`.
124 """
Armin Ronacher24b65582008-05-26 13:35:58 +0200125 if __debug__:
126 __traceback_hide__ = True
127 if isinstance(__obj, _context_function_types):
128 if getattr(__obj, 'contextfunction', 0):
129 args = (__self,) + args
130 elif getattr(__obj, 'environmentfunction', 0):
131 args = (__self.environment,) + args
Armin Ronacherfd310492008-05-25 00:16:51 +0200132 return __obj(*args, **kwargs)
133
Armin Ronacherf35e2812008-05-06 16:04:10 +0200134 def _all(meth):
Armin Ronachere9411b42008-05-15 16:22:07 +0200135 proxy = lambda self: getattr(self.get_all(), meth)()
Armin Ronacherf35e2812008-05-06 16:04:10 +0200136 proxy.__doc__ = getattr(dict, meth).__doc__
137 proxy.__name__ = meth
138 return proxy
139
140 keys = _all('keys')
141 values = _all('values')
142 items = _all('items')
143 iterkeys = _all('iterkeys')
144 itervalues = _all('itervalues')
145 iteritems = _all('iteritems')
146 del _all
147
Armin Ronacherb5124e62008-04-25 00:36:14 +0200148 def __contains__(self, name):
149 return name in self.vars or name in self.parent
150
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200151 def __getitem__(self, key):
Armin Ronacherbbbe0622008-05-19 00:23:37 +0200152 """Lookup a variable or raise `KeyError` if the variable is
153 undefined.
154 """
155 item = self.resolve(key)
156 if isinstance(item, Undefined):
157 raise KeyError(key)
158 return item
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200159
Armin Ronacherf059ec12008-04-11 22:21:00 +0200160 def __repr__(self):
161 return '<%s %s of %r>' % (
162 self.__class__.__name__,
Armin Ronacher963f97d2008-04-25 11:44:59 +0200163 repr(self.get_all()),
Armin Ronacher68f77672008-04-17 11:50:39 +0200164 self.name
Armin Ronacherf059ec12008-04-11 22:21:00 +0200165 )
166
167
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200168# register the context as mutable mapping if possible
169try:
170 from collections import MutableMapping
171 MutableMapping.register(Context)
172except ImportError:
173 pass
174
175
Armin Ronacherc9705c22008-04-27 21:28:03 +0200176class TemplateReference(object):
177 """The `self` in templates."""
Armin Ronacher62f8a292008-04-13 23:18:05 +0200178
Armin Ronacherc9705c22008-04-27 21:28:03 +0200179 def __init__(self, context):
180 self.__context = context
Armin Ronacher62f8a292008-04-13 23:18:05 +0200181
Armin Ronacherc9705c22008-04-27 21:28:03 +0200182 def __getitem__(self, name):
Armin Ronacher6df604e2008-05-23 22:18:38 +0200183 func = self.__context.blocks[name][0]
Armin Ronacherd1342312008-04-28 12:20:12 +0200184 wrap = self.__context.environment.autoescape and \
185 Markup or (lambda x: x)
186 render = lambda: wrap(concat(func(self.__context)))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200187 render.__name__ = render.name = name
188 return render
Armin Ronacher62f8a292008-04-13 23:18:05 +0200189
190 def __repr__(self):
191 return '<%s %r>' % (
192 self.__class__.__name__,
Armin Ronacherc9705c22008-04-27 21:28:03 +0200193 self._context.name
Armin Ronacher62f8a292008-04-13 23:18:05 +0200194 )
195
196
Armin Ronacher32a910f2008-04-26 23:21:03 +0200197class LoopContext(object):
198 """A loop context for dynamic iteration."""
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200199
Armin Ronacher547d0b62008-07-04 16:35:10 +0200200 def __init__(self, iterable, recurse=None):
201 self._iterator = iter(iterable)
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200202 self._recurse = recurse
Armin Ronacher32a910f2008-04-26 23:21:03 +0200203 self.index0 = -1
Armin Ronacher547d0b62008-07-04 16:35:10 +0200204
205 # try to get the length of the iterable early. This must be done
206 # here because there are some broken iterators around where there
207 # __len__ is the number of iterations left (i'm looking at your
208 # listreverseiterator!).
209 try:
210 self._length = len(iterable)
211 except (TypeError, AttributeError):
212 self._length = None
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200213
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200214 def cycle(self, *args):
Armin Ronachered1e0d42008-05-18 20:25:28 +0200215 """Cycles among the arguments with the current loop index."""
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200216 if not args:
217 raise TypeError('no items for cycling given')
218 return args[self.index0 % len(args)]
219
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200220 first = property(lambda x: x.index0 == 0)
Armin Ronacher547d0b62008-07-04 16:35:10 +0200221 last = property(lambda x: x.index0 + 1 == x.length)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200222 index = property(lambda x: x.index0 + 1)
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200223 revindex = property(lambda x: x.length - x.index0)
224 revindex0 = property(lambda x: x.length - x.index)
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200225
226 def __len__(self):
227 return self.length
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200228
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200229 def __iter__(self):
Armin Ronachered1e0d42008-05-18 20:25:28 +0200230 return LoopContextIterator(self)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200231
Armin Ronacher66a93442008-05-11 23:42:19 +0200232 def loop(self, iterable):
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200233 if self._recurse is None:
234 raise TypeError('Tried to call non recursive loop. Maybe you '
Armin Ronacher66a93442008-05-11 23:42:19 +0200235 "forgot the 'recursive' modifier.")
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200236 return self._recurse(iterable, self._recurse)
237
Armin Ronacher66a93442008-05-11 23:42:19 +0200238 # a nifty trick to enhance the error message if someone tried to call
239 # the the loop without or with too many arguments.
240 __call__ = loop; del loop
241
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200242 @property
243 def length(self):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200244 if self._length is None:
Armin Ronacher547d0b62008-07-04 16:35:10 +0200245 # if was not possible to get the length of the iterator when
246 # the loop context was created (ie: iterating over a generator)
247 # we have to convert the iterable into a sequence and use the
248 # length of that.
249 iterable = tuple(self._iterator)
250 self._iterator = iter(iterable)
251 self._length = len(iterable) + self.index0 + 1
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200252 return self._length
253
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200254 def __repr__(self):
Armin Ronacherc9705c22008-04-27 21:28:03 +0200255 return '<%s %r/%r>' % (
256 self.__class__.__name__,
257 self.index,
258 self.length
259 )
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200260
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200261
Armin Ronachered1e0d42008-05-18 20:25:28 +0200262class LoopContextIterator(object):
263 """The iterator for a loop context."""
264 __slots__ = ('context',)
265
266 def __init__(self, context):
267 self.context = context
268
269 def __iter__(self):
270 return self
271
272 def next(self):
273 ctx = self.context
274 ctx.index0 += 1
Armin Ronacher547d0b62008-07-04 16:35:10 +0200275 return ctx._iterator.next(), ctx
Armin Ronachered1e0d42008-05-18 20:25:28 +0200276
277
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200278class Macro(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200279 """Wraps a macro."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200280
Armin Ronacher963f97d2008-04-25 11:44:59 +0200281 def __init__(self, environment, func, name, arguments, defaults,
282 catch_kwargs, catch_varargs, caller):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200283 self._environment = environment
Armin Ronacher71082072008-04-12 14:19:36 +0200284 self._func = func
Armin Ronacherd84ec462008-04-29 13:43:16 +0200285 self._argument_count = len(arguments)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200286 self.name = name
287 self.arguments = arguments
288 self.defaults = defaults
Armin Ronacher963f97d2008-04-25 11:44:59 +0200289 self.catch_kwargs = catch_kwargs
290 self.catch_varargs = catch_varargs
Armin Ronacher71082072008-04-12 14:19:36 +0200291 self.caller = caller
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200292
293 def __call__(self, *args, **kwargs):
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200294 arguments = []
Armin Ronacher9706fab2008-04-08 18:49:56 +0200295 for idx, name in enumerate(self.arguments):
296 try:
297 value = args[idx]
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200298 except:
Armin Ronacher9706fab2008-04-08 18:49:56 +0200299 try:
300 value = kwargs.pop(name)
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200301 except:
Armin Ronacher9706fab2008-04-08 18:49:56 +0200302 try:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200303 value = self.defaults[idx - self._argument_count]
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200304 except:
Armin Ronacher9a822052008-04-17 18:44:07 +0200305 value = self._environment.undefined(
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200306 'parameter %r was not provided' % name, name=name)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200307 arguments.append(value)
308
309 # it's important that the order of these arguments does not change
310 # if not also changed in the compiler's `function_scoping` method.
311 # the order is caller, keyword arguments, positional arguments!
Armin Ronacher71082072008-04-12 14:19:36 +0200312 if self.caller:
313 caller = kwargs.pop('caller', None)
314 if caller is None:
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200315 caller = self._environment.undefined('No caller defined',
316 name='caller')
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200317 arguments.append(caller)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200318 if self.catch_kwargs:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200319 arguments.append(kwargs)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200320 elif kwargs:
321 raise TypeError('macro %r takes no keyword argument %r' %
322 (self.name, iter(kwargs).next()))
323 if self.catch_varargs:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200324 arguments.append(args[self._argument_count:])
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200325 elif len(args) > self._argument_count:
326 raise TypeError('macro %r takes not more than %d argument(s)' %
327 (self.name, len(self.arguments)))
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200328 return self._func(*arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200329
330 def __repr__(self):
331 return '<%s %s>' % (
332 self.__class__.__name__,
333 self.name is None and 'anonymous' or repr(self.name)
334 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200335
336
337class Undefined(object):
Armin Ronacherd1342312008-04-28 12:20:12 +0200338 """The default undefined type. This undefined type can be printed and
339 iterated over, but every other access will raise an :exc:`UndefinedError`:
340
341 >>> foo = Undefined(name='foo')
342 >>> str(foo)
343 ''
344 >>> not foo
345 True
346 >>> foo + 42
347 Traceback (most recent call last):
348 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200349 UndefinedError: 'foo' is undefined
Armin Ronacherc63243e2008-04-14 22:53:58 +0200350 """
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200351 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
352 '_undefined_exception')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200353
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200354 def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
Armin Ronacher9a822052008-04-17 18:44:07 +0200355 self._undefined_hint = hint
356 self._undefined_obj = obj
357 self._undefined_name = name
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200358 self._undefined_exception = exc
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200359
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200360 def _fail_with_undefined_error(self, *args, **kwargs):
361 """Regular callback function for undefined objects that raises an
362 `UndefinedError` on call.
363 """
364 if self._undefined_hint is None:
365 if self._undefined_obj is None:
366 hint = '%r is undefined' % self._undefined_name
367 elif not isinstance(self._undefined_name, basestring):
368 hint = '%r object has no element %r' % (
369 self._undefined_obj.__class__.__name__,
370 self._undefined_name
371 )
372 else:
373 hint = '%r object has no attribute %r' % (
374 self._undefined_obj.__class__.__name__,
375 self._undefined_name
376 )
377 else:
378 hint = self._undefined_hint
379 raise self._undefined_exception(hint)
380
Armin Ronacherc63243e2008-04-14 22:53:58 +0200381 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
382 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
383 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher53042292008-04-26 18:30:19 +0200384 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200385 __int__ = __float__ = __complex__ = _fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200386
387 def __str__(self):
388 return self.__unicode__().encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200389
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200390 def __unicode__(self):
391 return u''
392
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200393 def __len__(self):
394 return 0
395
396 def __iter__(self):
397 if 0:
398 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200399
400 def __nonzero__(self):
401 return False
402
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200403 def __repr__(self):
404 return 'Undefined'
405
Armin Ronacherc63243e2008-04-14 22:53:58 +0200406
407class DebugUndefined(Undefined):
Armin Ronacherd1342312008-04-28 12:20:12 +0200408 """An undefined that returns the debug info when printed.
409
410 >>> foo = DebugUndefined(name='foo')
411 >>> str(foo)
412 '{{ foo }}'
413 >>> not foo
414 True
415 >>> foo + 42
416 Traceback (most recent call last):
417 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200418 UndefinedError: 'foo' is undefined
Armin Ronacherd1342312008-04-28 12:20:12 +0200419 """
Armin Ronacher53042292008-04-26 18:30:19 +0200420 __slots__ = ()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200421
422 def __unicode__(self):
Armin Ronacher9a822052008-04-17 18:44:07 +0200423 if self._undefined_hint is None:
424 if self._undefined_obj is None:
425 return u'{{ %s }}' % self._undefined_name
426 return '{{ no such element: %s[%r] }}' % (
427 self._undefined_obj.__class__.__name__,
428 self._undefined_name
429 )
430 return u'{{ undefined value printed: %s }}' % self._undefined_hint
Armin Ronacherc63243e2008-04-14 22:53:58 +0200431
432
433class StrictUndefined(Undefined):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200434 """An undefined that barks on print and iteration as well as boolean
Armin Ronacher53042292008-04-26 18:30:19 +0200435 tests and all kinds of comparisons. In other words: you can do nothing
436 with it except checking if it's defined using the `defined` test.
Armin Ronacherd1342312008-04-28 12:20:12 +0200437
438 >>> foo = StrictUndefined(name='foo')
439 >>> str(foo)
440 Traceback (most recent call last):
441 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200442 UndefinedError: 'foo' is undefined
Armin Ronacherd1342312008-04-28 12:20:12 +0200443 >>> not foo
444 Traceback (most recent call last):
445 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200446 UndefinedError: 'foo' is undefined
Armin Ronacherd1342312008-04-28 12:20:12 +0200447 >>> foo + 42
448 Traceback (most recent call last):
449 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200450 UndefinedError: 'foo' is undefined
Priit Laes4149a0e2008-04-17 19:04:44 +0200451 """
Armin Ronacher53042292008-04-26 18:30:19 +0200452 __slots__ = ()
453 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200454 Undefined._fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200455
Armin Ronacher53042292008-04-26 18:30:19 +0200456
457# remove remaining slots attributes, after the metaclass did the magic they
458# are unneeded and irritating as they contain wrong data for the subclasses.
459del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__