blob: f8c251bdd7ac4d3e70060c93c79c09f76aded15f [file] [log] [blame]
Armin Ronachere791c2a2008-04-07 18:39:54 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.runtime
4 ~~~~~~~~~~~~~~
5
6 Runtime helpers.
7
Armin Ronacher62ccd1b2009-01-04 14:26:19 +01008 :copyright: (c) 2009 by the Jinja Team.
Armin Ronacherd7764372008-07-15 00:11:14 +02009 :license: BSD.
Armin Ronachere791c2a2008-04-07 18:39:54 +020010"""
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020011import sys
Armin Ronacherd1342312008-04-28 12:20:12 +020012from itertools import chain, imap
Armin Ronacherce677102008-08-17 19:43:22 +020013from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
Armin Ronacherbd357722009-08-05 20:25:06 +020014 concat, MethodType, FunctionType, internalcode, next
Armin Ronacher37f58ce2008-12-27 13:10:38 +010015from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
16 TemplateNotFound
Armin Ronachere791c2a2008-04-07 18:39:54 +020017
18
Armin Ronacher2feed1d2008-04-26 16:26:52 +020019# these variables are exported to the template runtime
Armin Ronacher74a0cd92009-02-19 15:56:53 +010020__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
Armin Ronacher19cf9c22008-05-01 12:49:53 +020021 'TemplateRuntimeError', 'missing', 'concat', 'escape',
Armin Ronacherbd357722009-08-05 20:25:06 +020022 'markup_join', 'unicode_join', 'to_string',
23 'TemplateNotFound']
Armin Ronacher0611e492008-04-25 23:44:14 +020024
Armin Ronacher9a0078d2008-08-13 18:24:17 +020025
Armin Ronacherce677102008-08-17 19:43:22 +020026#: the types we support for context functions
27_context_function_types = (FunctionType, MethodType)
Armin Ronacher24b65582008-05-26 13:35:58 +020028
Armin Ronacherbd357722009-08-05 20:25:06 +020029#: the name of the function that is used to convert something into
30#: a string. 2to3 will adopt that automatically and the generated
31#: code can take advantage of it.
32to_string = unicode
33
Armin Ronacher0611e492008-04-25 23:44:14 +020034
Armin Ronacherdc02b642008-05-15 22:47:27 +020035def markup_join(seq):
Armin Ronacherd1342312008-04-28 12:20:12 +020036 """Concatenation that escapes if necessary and converts to unicode."""
37 buf = []
Armin Ronacherdc02b642008-05-15 22:47:27 +020038 iterator = imap(soft_unicode, seq)
Armin Ronacherd1342312008-04-28 12:20:12 +020039 for arg in iterator:
40 buf.append(arg)
41 if hasattr(arg, '__html__'):
42 return Markup(u'').join(chain(buf, iterator))
43 return concat(buf)
44
45
Armin Ronacherdc02b642008-05-15 22:47:27 +020046def unicode_join(seq):
Armin Ronacherd1342312008-04-28 12:20:12 +020047 """Simple args to unicode conversion and concatenation."""
Armin Ronacherdc02b642008-05-15 22:47:27 +020048 return concat(imap(unicode, seq))
Armin Ronacherd1342312008-04-28 12:20:12 +020049
50
Armin Ronacher74a0cd92009-02-19 15:56:53 +010051def new_context(environment, template_name, blocks, vars=None,
52 shared=None, globals=None, locals=None):
53 """Internal helper to for context creation."""
54 if vars is None:
55 vars = {}
56 if shared:
57 parent = vars
58 else:
59 parent = dict(globals or (), **vars)
60 if locals:
61 # if the parent is shared a copy should be created because
62 # we don't want to modify the dict passed
63 if shared:
64 parent = dict(parent)
65 for key, value in locals.iteritems():
66 if key[:2] == 'l_' and value is not missing:
67 parent[key[2:]] = value
68 return Context(environment, parent, template_name, blocks)
69
70
71class TemplateReference(object):
72 """The `self` in templates."""
73
74 def __init__(self, context):
75 self.__context = context
76
77 def __getitem__(self, name):
78 blocks = self.__context.blocks[name]
79 wrap = self.__context.environment.autoescape and \
80 Markup or (lambda x: x)
81 return BlockReference(name, self.__context, blocks, 0)
82
83 def __repr__(self):
84 return '<%s %r>' % (
85 self.__class__.__name__,
86 self.__context.name
87 )
88
89
Armin Ronacher19cf9c22008-05-01 12:49:53 +020090class Context(object):
Armin Ronacher7259c762008-04-30 13:03:59 +020091 """The template context holds the variables of a template. It stores the
92 values passed to the template and also the names the template exports.
93 Creating instances is neither supported nor useful as it's created
94 automatically at various stages of the template evaluation and should not
95 be created by hand.
Armin Ronacherc9705c22008-04-27 21:28:03 +020096
Armin Ronacher7259c762008-04-30 13:03:59 +020097 The context is immutable. Modifications on :attr:`parent` **must not**
98 happen and modifications on :attr:`vars` are allowed from generated
99 template code only. Template filters and global functions marked as
100 :func:`contextfunction`\s get the active context passed as first argument
101 and are allowed to access the context read-only.
102
103 The template context supports read only dict operations (`get`,
Armin Ronacherf35e2812008-05-06 16:04:10 +0200104 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
105 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
106 method that doesn't fail with a `KeyError` but returns an
107 :class:`Undefined` object for missing variables.
Armin Ronacher9706fab2008-04-08 18:49:56 +0200108 """
Armin Ronacher771c7502008-05-18 23:14:14 +0200109 __slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
Armin Ronacherdcc217c2008-09-18 18:38:58 +0200110 'blocks', '__weakref__')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200111
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200112 def __init__(self, environment, parent, name, blocks):
113 self.parent = parent
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200114 self.vars = vars = {}
Armin Ronacherc63243e2008-04-14 22:53:58 +0200115 self.environment = environment
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200116 self.exported_vars = set()
Armin Ronacher68f77672008-04-17 11:50:39 +0200117 self.name = name
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200118
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200119 # create the initial mapping of blocks. Whenever template inheritance
120 # takes place the runtime will update this mapping with the new blocks
121 # from the template.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200122 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
Armin Ronacherf059ec12008-04-11 22:21:00 +0200123
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200124 def super(self, name, current):
Armin Ronacher62f8a292008-04-13 23:18:05 +0200125 """Render a parent block."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200126 try:
127 blocks = self.blocks[name]
Armin Ronacherc347ed02008-09-20 12:04:53 +0200128 index = blocks.index(current) + 1
129 blocks[index]
Armin Ronacherc9705c22008-04-27 21:28:03 +0200130 except LookupError:
Armin Ronacher9a822052008-04-17 18:44:07 +0200131 return self.environment.undefined('there is no parent block '
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200132 'called %r.' % name,
133 name='super')
Armin Ronacherc347ed02008-09-20 12:04:53 +0200134 return BlockReference(name, self, blocks, index)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200135
Armin Ronacher53042292008-04-26 18:30:19 +0200136 def get(self, key, default=None):
Armin Ronacher7259c762008-04-30 13:03:59 +0200137 """Returns an item from the template context, if it doesn't exist
138 `default` is returned.
139 """
Armin Ronacherf35e2812008-05-06 16:04:10 +0200140 try:
141 return self[key]
142 except KeyError:
143 return default
144
145 def resolve(self, key):
146 """Looks up a variable like `__getitem__` or `get` but returns an
147 :class:`Undefined` object with the name of the name looked up.
148 """
Armin Ronacher53042292008-04-26 18:30:19 +0200149 if key in self.vars:
150 return self.vars[key]
151 if key in self.parent:
152 return self.parent[key]
Armin Ronacherf35e2812008-05-06 16:04:10 +0200153 return self.environment.undefined(name=key)
Armin Ronacherb5124e62008-04-25 00:36:14 +0200154
Armin Ronacher9706fab2008-04-08 18:49:56 +0200155 def get_exported(self):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200156 """Get a new dict with the exported variables."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200157 return dict((k, self.vars[k]) for k in self.exported_vars)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200158
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200159 def get_all(self):
Armin Ronacher7259c762008-04-30 13:03:59 +0200160 """Return a copy of the complete context as dict including the
Armin Ronacher5411ce72008-05-25 11:36:22 +0200161 exported variables.
Armin Ronacher7259c762008-04-30 13:03:59 +0200162 """
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200163 return dict(self.parent, **self.vars)
164
Armin Ronacherd416a972009-02-24 22:58:00 +0100165 @internalcode
Armin Ronacherfd310492008-05-25 00:16:51 +0200166 def call(__self, __obj, *args, **kwargs):
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200167 """Call the callable with the arguments and keyword arguments
168 provided but inject the active context or environment as first
169 argument if the callable is a :func:`contextfunction` or
170 :func:`environmentfunction`.
171 """
Armin Ronacher24b65582008-05-26 13:35:58 +0200172 if __debug__:
173 __traceback_hide__ = True
174 if isinstance(__obj, _context_function_types):
175 if getattr(__obj, 'contextfunction', 0):
176 args = (__self,) + args
177 elif getattr(__obj, 'environmentfunction', 0):
178 args = (__self.environment,) + args
Armin Ronacherfd310492008-05-25 00:16:51 +0200179 return __obj(*args, **kwargs)
180
Armin Ronacher74a0cd92009-02-19 15:56:53 +0100181 def derived(self, locals=None):
182 """Internal helper function to create a derived context."""
Armin Ronacher3f1d8f12009-02-19 16:11:11 +0100183 context = new_context(self.environment, self.name, {},
184 self.parent, True, None, locals)
185 context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
186 return context
Armin Ronacher74a0cd92009-02-19 15:56:53 +0100187
Armin Ronacherf35e2812008-05-06 16:04:10 +0200188 def _all(meth):
Armin Ronachere9411b42008-05-15 16:22:07 +0200189 proxy = lambda self: getattr(self.get_all(), meth)()
Armin Ronacherf35e2812008-05-06 16:04:10 +0200190 proxy.__doc__ = getattr(dict, meth).__doc__
191 proxy.__name__ = meth
192 return proxy
193
194 keys = _all('keys')
195 values = _all('values')
196 items = _all('items')
Armin Ronacher42a19882009-08-05 18:45:39 +0200197
198 # not available on python 3
199 if hasattr(dict, 'iterkeys'):
200 iterkeys = _all('iterkeys')
201 itervalues = _all('itervalues')
202 iteritems = _all('iteritems')
Armin Ronacherf35e2812008-05-06 16:04:10 +0200203 del _all
204
Armin Ronacherb5124e62008-04-25 00:36:14 +0200205 def __contains__(self, name):
206 return name in self.vars or name in self.parent
207
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200208 def __getitem__(self, key):
Armin Ronacherbbbe0622008-05-19 00:23:37 +0200209 """Lookup a variable or raise `KeyError` if the variable is
210 undefined.
211 """
212 item = self.resolve(key)
213 if isinstance(item, Undefined):
214 raise KeyError(key)
215 return item
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200216
Armin Ronacherf059ec12008-04-11 22:21:00 +0200217 def __repr__(self):
218 return '<%s %s of %r>' % (
219 self.__class__.__name__,
Armin Ronacher963f97d2008-04-25 11:44:59 +0200220 repr(self.get_all()),
Armin Ronacher68f77672008-04-17 11:50:39 +0200221 self.name
Armin Ronacherf059ec12008-04-11 22:21:00 +0200222 )
223
224
Armin Ronacherccae0552008-10-05 23:08:58 +0200225# register the context as mapping if possible
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200226try:
Armin Ronacherccae0552008-10-05 23:08:58 +0200227 from collections import Mapping
228 Mapping.register(Context)
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200229except ImportError:
230 pass
231
232
Armin Ronacherc347ed02008-09-20 12:04:53 +0200233class BlockReference(object):
234 """One block on a template reference."""
235
236 def __init__(self, name, context, stack, depth):
237 self.name = name
238 self._context = context
239 self._stack = stack
240 self._depth = depth
241
242 @property
243 def super(self):
244 """Super the block."""
245 if self._depth + 1 >= len(self._stack):
246 return self._context.environment. \
247 undefined('there is no parent block called %r.' %
248 self.name, name='super')
249 return BlockReference(self.name, self._context, self._stack,
250 self._depth + 1)
251
Armin Ronacherd416a972009-02-24 22:58:00 +0100252 @internalcode
Armin Ronacherc347ed02008-09-20 12:04:53 +0200253 def __call__(self):
254 rv = concat(self._stack[self._depth](self._context))
255 if self._context.environment.autoescape:
256 rv = Markup(rv)
257 return rv
258
259
Armin Ronacher32a910f2008-04-26 23:21:03 +0200260class LoopContext(object):
261 """A loop context for dynamic iteration."""
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200262
Armin Ronacher547d0b62008-07-04 16:35:10 +0200263 def __init__(self, iterable, recurse=None):
264 self._iterator = iter(iterable)
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200265 self._recurse = recurse
Armin Ronacher32a910f2008-04-26 23:21:03 +0200266 self.index0 = -1
Armin Ronacher547d0b62008-07-04 16:35:10 +0200267
268 # try to get the length of the iterable early. This must be done
269 # here because there are some broken iterators around where there
270 # __len__ is the number of iterations left (i'm looking at your
271 # listreverseiterator!).
272 try:
273 self._length = len(iterable)
274 except (TypeError, AttributeError):
275 self._length = None
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200276
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200277 def cycle(self, *args):
Armin Ronachered1e0d42008-05-18 20:25:28 +0200278 """Cycles among the arguments with the current loop index."""
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200279 if not args:
280 raise TypeError('no items for cycling given')
281 return args[self.index0 % len(args)]
282
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200283 first = property(lambda x: x.index0 == 0)
Armin Ronacher547d0b62008-07-04 16:35:10 +0200284 last = property(lambda x: x.index0 + 1 == x.length)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200285 index = property(lambda x: x.index0 + 1)
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200286 revindex = property(lambda x: x.length - x.index0)
287 revindex0 = property(lambda x: x.length - x.index)
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200288
289 def __len__(self):
290 return self.length
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200291
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200292 def __iter__(self):
Armin Ronachered1e0d42008-05-18 20:25:28 +0200293 return LoopContextIterator(self)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200294
Armin Ronacherd416a972009-02-24 22:58:00 +0100295 @internalcode
Armin Ronacher66a93442008-05-11 23:42:19 +0200296 def loop(self, iterable):
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200297 if self._recurse is None:
298 raise TypeError('Tried to call non recursive loop. Maybe you '
Armin Ronacher66a93442008-05-11 23:42:19 +0200299 "forgot the 'recursive' modifier.")
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200300 return self._recurse(iterable, self._recurse)
301
Armin Ronacher66a93442008-05-11 23:42:19 +0200302 # a nifty trick to enhance the error message if someone tried to call
303 # the the loop without or with too many arguments.
304 __call__ = loop; del loop
305
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200306 @property
307 def length(self):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200308 if self._length is None:
Armin Ronacher547d0b62008-07-04 16:35:10 +0200309 # if was not possible to get the length of the iterator when
310 # the loop context was created (ie: iterating over a generator)
311 # we have to convert the iterable into a sequence and use the
312 # length of that.
313 iterable = tuple(self._iterator)
314 self._iterator = iter(iterable)
315 self._length = len(iterable) + self.index0 + 1
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200316 return self._length
317
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200318 def __repr__(self):
Armin Ronacherc9705c22008-04-27 21:28:03 +0200319 return '<%s %r/%r>' % (
320 self.__class__.__name__,
321 self.index,
322 self.length
323 )
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200324
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200325
Armin Ronachered1e0d42008-05-18 20:25:28 +0200326class LoopContextIterator(object):
327 """The iterator for a loop context."""
328 __slots__ = ('context',)
329
330 def __init__(self, context):
331 self.context = context
332
333 def __iter__(self):
334 return self
335
336 def next(self):
337 ctx = self.context
338 ctx.index0 += 1
Armin Ronacherbd357722009-08-05 20:25:06 +0200339 return next(ctx._iterator), ctx
Armin Ronachered1e0d42008-05-18 20:25:28 +0200340
341
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200342class Macro(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200343 """Wraps a macro."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200344
Armin Ronacher963f97d2008-04-25 11:44:59 +0200345 def __init__(self, environment, func, name, arguments, defaults,
346 catch_kwargs, catch_varargs, caller):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200347 self._environment = environment
Armin Ronacher71082072008-04-12 14:19:36 +0200348 self._func = func
Armin Ronacherd84ec462008-04-29 13:43:16 +0200349 self._argument_count = len(arguments)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200350 self.name = name
351 self.arguments = arguments
352 self.defaults = defaults
Armin Ronacher963f97d2008-04-25 11:44:59 +0200353 self.catch_kwargs = catch_kwargs
354 self.catch_varargs = catch_varargs
Armin Ronacher71082072008-04-12 14:19:36 +0200355 self.caller = caller
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200356
Armin Ronacherd416a972009-02-24 22:58:00 +0100357 @internalcode
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200358 def __call__(self, *args, **kwargs):
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200359 arguments = []
Armin Ronacher9706fab2008-04-08 18:49:56 +0200360 for idx, name in enumerate(self.arguments):
361 try:
362 value = args[idx]
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200363 except:
Armin Ronacher9706fab2008-04-08 18:49:56 +0200364 try:
365 value = kwargs.pop(name)
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200366 except:
Armin Ronacher9706fab2008-04-08 18:49:56 +0200367 try:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200368 value = self.defaults[idx - self._argument_count]
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200369 except:
Armin Ronacher9a822052008-04-17 18:44:07 +0200370 value = self._environment.undefined(
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200371 'parameter %r was not provided' % name, name=name)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200372 arguments.append(value)
373
374 # it's important that the order of these arguments does not change
375 # if not also changed in the compiler's `function_scoping` method.
376 # the order is caller, keyword arguments, positional arguments!
Armin Ronacher71082072008-04-12 14:19:36 +0200377 if self.caller:
378 caller = kwargs.pop('caller', None)
379 if caller is None:
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200380 caller = self._environment.undefined('No caller defined',
381 name='caller')
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200382 arguments.append(caller)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200383 if self.catch_kwargs:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200384 arguments.append(kwargs)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200385 elif kwargs:
386 raise TypeError('macro %r takes no keyword argument %r' %
Armin Ronacherbd357722009-08-05 20:25:06 +0200387 (self.name, next(iter(kwargs))))
Armin Ronacher963f97d2008-04-25 11:44:59 +0200388 if self.catch_varargs:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200389 arguments.append(args[self._argument_count:])
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200390 elif len(args) > self._argument_count:
391 raise TypeError('macro %r takes not more than %d argument(s)' %
392 (self.name, len(self.arguments)))
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200393 return self._func(*arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200394
395 def __repr__(self):
396 return '<%s %s>' % (
397 self.__class__.__name__,
398 self.name is None and 'anonymous' or repr(self.name)
399 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200400
401
402class Undefined(object):
Armin Ronacherd1342312008-04-28 12:20:12 +0200403 """The default undefined type. This undefined type can be printed and
404 iterated over, but every other access will raise an :exc:`UndefinedError`:
405
406 >>> foo = Undefined(name='foo')
407 >>> str(foo)
408 ''
409 >>> not foo
410 True
411 >>> foo + 42
412 Traceback (most recent call last):
413 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200414 UndefinedError: 'foo' is undefined
Armin Ronacherc63243e2008-04-14 22:53:58 +0200415 """
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200416 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
417 '_undefined_exception')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200418
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200419 def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
Armin Ronacher9a822052008-04-17 18:44:07 +0200420 self._undefined_hint = hint
421 self._undefined_obj = obj
422 self._undefined_name = name
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200423 self._undefined_exception = exc
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200424
Armin Ronacherd416a972009-02-24 22:58:00 +0100425 @internalcode
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200426 def _fail_with_undefined_error(self, *args, **kwargs):
427 """Regular callback function for undefined objects that raises an
428 `UndefinedError` on call.
429 """
430 if self._undefined_hint is None:
431 if self._undefined_obj is None:
432 hint = '%r is undefined' % self._undefined_name
433 elif not isinstance(self._undefined_name, basestring):
434 hint = '%r object has no element %r' % (
435 self._undefined_obj.__class__.__name__,
436 self._undefined_name
437 )
438 else:
439 hint = '%r object has no attribute %r' % (
440 self._undefined_obj.__class__.__name__,
441 self._undefined_name
442 )
443 else:
444 hint = self._undefined_hint
445 raise self._undefined_exception(hint)
446
Armin Ronacherc63243e2008-04-14 22:53:58 +0200447 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
Armin Ronacher9efe0812008-11-02 12:22:00 +0100448 __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
Armin Ronacherc63243e2008-04-14 22:53:58 +0200449 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher53042292008-04-26 18:30:19 +0200450 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
Armin Ronacher9efe0812008-11-02 12:22:00 +0100451 __int__ = __float__ = __complex__ = __pow__ = __rpow__ = \
452 _fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200453
454 def __str__(self):
Armin Ronacherccae0552008-10-05 23:08:58 +0200455 return unicode(self).encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200456
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200457 def __unicode__(self):
458 return u''
459
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200460 def __len__(self):
461 return 0
462
463 def __iter__(self):
464 if 0:
465 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200466
467 def __nonzero__(self):
468 return False
469
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200470 def __repr__(self):
471 return 'Undefined'
472
Armin Ronacherc63243e2008-04-14 22:53:58 +0200473
474class DebugUndefined(Undefined):
Armin Ronacherd1342312008-04-28 12:20:12 +0200475 """An undefined that returns the debug info when printed.
476
477 >>> foo = DebugUndefined(name='foo')
478 >>> str(foo)
479 '{{ foo }}'
480 >>> not foo
481 True
482 >>> foo + 42
483 Traceback (most recent call last):
484 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200485 UndefinedError: 'foo' is undefined
Armin Ronacherd1342312008-04-28 12:20:12 +0200486 """
Armin Ronacher53042292008-04-26 18:30:19 +0200487 __slots__ = ()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200488
489 def __unicode__(self):
Armin Ronacher9a822052008-04-17 18:44:07 +0200490 if self._undefined_hint is None:
491 if self._undefined_obj is None:
492 return u'{{ %s }}' % self._undefined_name
493 return '{{ no such element: %s[%r] }}' % (
494 self._undefined_obj.__class__.__name__,
495 self._undefined_name
496 )
497 return u'{{ undefined value printed: %s }}' % self._undefined_hint
Armin Ronacherc63243e2008-04-14 22:53:58 +0200498
499
500class StrictUndefined(Undefined):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200501 """An undefined that barks on print and iteration as well as boolean
Armin Ronacher53042292008-04-26 18:30:19 +0200502 tests and all kinds of comparisons. In other words: you can do nothing
503 with it except checking if it's defined using the `defined` test.
Armin Ronacherd1342312008-04-28 12:20:12 +0200504
505 >>> foo = StrictUndefined(name='foo')
506 >>> str(foo)
507 Traceback (most recent call last):
508 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200509 UndefinedError: 'foo' is undefined
Armin Ronacherd1342312008-04-28 12:20:12 +0200510 >>> not foo
511 Traceback (most recent call last):
512 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200513 UndefinedError: 'foo' is undefined
Armin Ronacherd1342312008-04-28 12:20:12 +0200514 >>> foo + 42
515 Traceback (most recent call last):
516 ...
Armin Ronacherabd36572008-06-27 08:45:19 +0200517 UndefinedError: 'foo' is undefined
Priit Laes4149a0e2008-04-17 19:04:44 +0200518 """
Armin Ronacher53042292008-04-26 18:30:19 +0200519 __slots__ = ()
520 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
Armin Ronacher9bb7e472008-05-28 11:26:59 +0200521 Undefined._fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200522
Armin Ronacher53042292008-04-26 18:30:19 +0200523
524# remove remaining slots attributes, after the metaclass did the magic they
525# are unneeded and irritating as they contain wrong data for the subclasses.
526del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__