blob: 417fa707ec84ffada328baf2077d526002bea510 [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 Ronacher4f7d2d52008-04-22 10:40:26 +020012from types import FunctionType
Armin Ronacherd1342312008-04-28 12:20:12 +020013from itertools import chain, imap
Armin Ronacher7259c762008-04-30 13:03:59 +020014from jinja2.utils import Markup, partial, soft_unicode, escape, missing
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
23
Armin Ronacherde6bf712008-04-26 01:44:14 +020024# concatenate a list of strings and convert them to unicode.
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020025# unfortunately there is a bug in python 2.4 and lower that causes
26# unicode.join trash the traceback.
27try:
28 def _test_gen_bug():
29 raise TypeError(_test_gen_bug)
30 yield None
31 u''.join(_test_gen_bug())
Armin Ronacher7259c762008-04-30 13:03:59 +020032except TypeError, _error:
33 if _error.args and _error.args[0] is _test_gen_bug:
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020034 concat = u''.join
35 else:
36 def concat(gen):
37 try:
Armin Ronacherd84ec462008-04-29 13:43:16 +020038 return u''.join(list(gen))
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020039 except:
40 exc_type, exc_value, tb = sys.exc_info()
41 raise exc_type, exc_value, tb.tb_next
Armin Ronacher7259c762008-04-30 13:03:59 +020042 del _test_gen_bug, _error
Armin Ronacherde6bf712008-04-26 01:44:14 +020043
44
Armin Ronacherd1342312008-04-28 12:20:12 +020045def markup_join(*args):
46 """Concatenation that escapes if necessary and converts to unicode."""
47 buf = []
48 iterator = imap(soft_unicode, args)
49 for arg in iterator:
50 buf.append(arg)
51 if hasattr(arg, '__html__'):
52 return Markup(u'').join(chain(buf, iterator))
53 return concat(buf)
54
55
56def unicode_join(*args):
57 """Simple args to unicode conversion and concatenation."""
58 return concat(imap(unicode, args))
59
60
Armin Ronacher19cf9c22008-05-01 12:49:53 +020061class Context(object):
Armin Ronacher7259c762008-04-30 13:03:59 +020062 """The template context holds the variables of a template. It stores the
63 values passed to the template and also the names the template exports.
64 Creating instances is neither supported nor useful as it's created
65 automatically at various stages of the template evaluation and should not
66 be created by hand.
Armin Ronacherc9705c22008-04-27 21:28:03 +020067
Armin Ronacher7259c762008-04-30 13:03:59 +020068 The context is immutable. Modifications on :attr:`parent` **must not**
69 happen and modifications on :attr:`vars` are allowed from generated
70 template code only. Template filters and global functions marked as
71 :func:`contextfunction`\s get the active context passed as first argument
72 and are allowed to access the context read-only.
73
74 The template context supports read only dict operations (`get`,
75 `__getitem__`, `__contains__`) however `__getitem__` doesn't fail with
76 a `KeyError` but returns an :attr:`Undefined` object.
Armin Ronacher9706fab2008-04-08 18:49:56 +020077 """
Armin Ronachere791c2a2008-04-07 18:39:54 +020078
Armin Ronacher203bfcb2008-04-24 21:54:44 +020079 def __init__(self, environment, parent, name, blocks):
80 self.parent = parent
Armin Ronacher2feed1d2008-04-26 16:26:52 +020081 self.vars = vars = {}
Armin Ronacherc63243e2008-04-14 22:53:58 +020082 self.environment = environment
Armin Ronacher203bfcb2008-04-24 21:54:44 +020083 self.exported_vars = set()
Armin Ronacher68f77672008-04-17 11:50:39 +020084 self.name = name
Armin Ronacher203bfcb2008-04-24 21:54:44 +020085
86 # bind functions to the context of environment if required
Armin Ronacher2feed1d2008-04-26 16:26:52 +020087 for name, obj in parent.iteritems():
Armin Ronacher203bfcb2008-04-24 21:54:44 +020088 if type(obj) is FunctionType:
89 if getattr(obj, 'contextfunction', 0):
Armin Ronacher2feed1d2008-04-26 16:26:52 +020090 vars[name] = partial(obj, self)
Armin Ronacher203bfcb2008-04-24 21:54:44 +020091 elif getattr(obj, 'environmentfunction', 0):
Armin Ronacher2feed1d2008-04-26 16:26:52 +020092 vars[name] = partial(obj, environment)
Armin Ronacher203bfcb2008-04-24 21:54:44 +020093
94 # create the initial mapping of blocks. Whenever template inheritance
95 # takes place the runtime will update this mapping with the new blocks
96 # from the template.
Armin Ronacher75cfb862008-04-11 13:47:22 +020097 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
Armin Ronacherf059ec12008-04-11 22:21:00 +020098
Armin Ronacher203bfcb2008-04-24 21:54:44 +020099 def super(self, name, current):
Armin Ronacher62f8a292008-04-13 23:18:05 +0200100 """Render a parent block."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200101 try:
102 blocks = self.blocks[name]
103 pos = blocks.index(current) - 1
104 if pos < 0:
105 raise IndexError()
106 except LookupError:
Armin Ronacher9a822052008-04-17 18:44:07 +0200107 return self.environment.undefined('there is no parent block '
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200108 'called %r.' % name,
109 name='super')
Armin Ronacherd1342312008-04-28 12:20:12 +0200110 wrap = self.environment.autoescape and Markup or (lambda x: x)
111 render = lambda: wrap(concat(blocks[pos](self)))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200112 render.__name__ = render.name = name
113 return render
Armin Ronachere791c2a2008-04-07 18:39:54 +0200114
Armin Ronacher53042292008-04-26 18:30:19 +0200115 def get(self, key, default=None):
Armin Ronacher7259c762008-04-30 13:03:59 +0200116 """Returns an item from the template context, if it doesn't exist
117 `default` is returned.
118 """
Armin Ronacher53042292008-04-26 18:30:19 +0200119 if key in self.vars:
120 return self.vars[key]
121 if key in self.parent:
122 return self.parent[key]
123 return default
Armin Ronacherb5124e62008-04-25 00:36:14 +0200124
Armin Ronacher9706fab2008-04-08 18:49:56 +0200125 def get_exported(self):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200126 """Get a new dict with the exported variables."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200127 return dict((k, self.vars[k]) for k in self.exported_vars)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200128
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200129 def get_all(self):
Armin Ronacher7259c762008-04-30 13:03:59 +0200130 """Return a copy of the complete context as dict including the
131 global variables.
132 """
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200133 return dict(self.parent, **self.vars)
134
Armin Ronacherb5124e62008-04-25 00:36:14 +0200135 def __contains__(self, name):
136 return name in self.vars or name in self.parent
137
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200138 def __getitem__(self, key):
139 if key in self.vars:
140 return self.vars[key]
Armin Ronacher53042292008-04-26 18:30:19 +0200141 if key in self.parent:
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200142 return self.parent[key]
Armin Ronacher53042292008-04-26 18:30:19 +0200143 return self.environment.undefined(name=key)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200144
Armin Ronacherf059ec12008-04-11 22:21:00 +0200145 def __repr__(self):
146 return '<%s %s of %r>' % (
147 self.__class__.__name__,
Armin Ronacher963f97d2008-04-25 11:44:59 +0200148 repr(self.get_all()),
Armin Ronacher68f77672008-04-17 11:50:39 +0200149 self.name
Armin Ronacherf059ec12008-04-11 22:21:00 +0200150 )
151
152
Armin Ronacherc9705c22008-04-27 21:28:03 +0200153class TemplateReference(object):
154 """The `self` in templates."""
Armin Ronacher62f8a292008-04-13 23:18:05 +0200155
Armin Ronacherc9705c22008-04-27 21:28:03 +0200156 def __init__(self, context):
157 self.__context = context
Armin Ronacher62f8a292008-04-13 23:18:05 +0200158
Armin Ronacherc9705c22008-04-27 21:28:03 +0200159 def __getitem__(self, name):
160 func = self.__context.blocks[name][-1]
Armin Ronacherd1342312008-04-28 12:20:12 +0200161 wrap = self.__context.environment.autoescape and \
162 Markup or (lambda x: x)
163 render = lambda: wrap(concat(func(self.__context)))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200164 render.__name__ = render.name = name
165 return render
Armin Ronacher62f8a292008-04-13 23:18:05 +0200166
167 def __repr__(self):
168 return '<%s %r>' % (
169 self.__class__.__name__,
Armin Ronacherc9705c22008-04-27 21:28:03 +0200170 self._context.name
Armin Ronacher62f8a292008-04-13 23:18:05 +0200171 )
172
173
Armin Ronacher32a910f2008-04-26 23:21:03 +0200174class LoopContext(object):
175 """A loop context for dynamic iteration."""
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200176
Armin Ronacher32a910f2008-04-26 23:21:03 +0200177 def __init__(self, iterable, enforce_length=False):
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200178 self._iterable = iterable
Armin Ronacher32a910f2008-04-26 23:21:03 +0200179 self._next = iter(iterable).next
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200180 self._length = None
Armin Ronacher32a910f2008-04-26 23:21:03 +0200181 self.index0 = -1
182 if enforce_length:
183 len(self)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200184
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200185 def cycle(self, *args):
186 """A replacement for the old ``{% cycle %}`` tag."""
187 if not args:
188 raise TypeError('no items for cycling given')
189 return args[self.index0 % len(args)]
190
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200191 first = property(lambda x: x.index0 == 0)
192 last = property(lambda x: x.revindex0 == 0)
193 index = property(lambda x: x.index0 + 1)
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200194 revindex = property(lambda x: x.length - x.index0)
195 revindex0 = property(lambda x: x.length - x.index)
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200196
197 def __len__(self):
198 return self.length
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200199
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200200 def __iter__(self):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200201 return self
202
203 def next(self):
204 self.index0 += 1
205 return self._next(), self
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200206
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200207 @property
208 def length(self):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200209 if self._length is None:
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200210 try:
211 length = len(self._iterable)
212 except TypeError:
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200213 self._iterable = tuple(self._iterable)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200214 self._next = iter(self._iterable).next
215 length = len(tuple(self._iterable)) + self.index0 + 1
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200216 self._length = length
217 return self._length
218
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200219 def __repr__(self):
Armin Ronacherc9705c22008-04-27 21:28:03 +0200220 return '<%s %r/%r>' % (
221 self.__class__.__name__,
222 self.index,
223 self.length
224 )
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200225
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200226
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200227class Macro(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200228 """Wraps a macro."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200229
Armin Ronacher963f97d2008-04-25 11:44:59 +0200230 def __init__(self, environment, func, name, arguments, defaults,
231 catch_kwargs, catch_varargs, caller):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200232 self._environment = environment
Armin Ronacher71082072008-04-12 14:19:36 +0200233 self._func = func
Armin Ronacherd84ec462008-04-29 13:43:16 +0200234 self._argument_count = len(arguments)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200235 self.name = name
236 self.arguments = arguments
237 self.defaults = defaults
Armin Ronacher963f97d2008-04-25 11:44:59 +0200238 self.catch_kwargs = catch_kwargs
239 self.catch_varargs = catch_varargs
Armin Ronacher71082072008-04-12 14:19:36 +0200240 self.caller = caller
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200241
242 def __call__(self, *args, **kwargs):
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200243 arguments = []
Armin Ronacher9706fab2008-04-08 18:49:56 +0200244 for idx, name in enumerate(self.arguments):
245 try:
246 value = args[idx]
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200247 except:
Armin Ronacher9706fab2008-04-08 18:49:56 +0200248 try:
249 value = kwargs.pop(name)
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200250 except:
Armin Ronacher9706fab2008-04-08 18:49:56 +0200251 try:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200252 value = self.defaults[idx - self._argument_count]
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200253 except:
Armin Ronacher9a822052008-04-17 18:44:07 +0200254 value = self._environment.undefined(
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200255 'parameter %r was not provided' % name, name=name)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200256 arguments.append(value)
257
258 # it's important that the order of these arguments does not change
259 # if not also changed in the compiler's `function_scoping` method.
260 # the order is caller, keyword arguments, positional arguments!
Armin Ronacher71082072008-04-12 14:19:36 +0200261 if self.caller:
262 caller = kwargs.pop('caller', None)
263 if caller is None:
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200264 caller = self._environment.undefined('No caller defined',
265 name='caller')
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200266 arguments.append(caller)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200267 if self.catch_kwargs:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200268 arguments.append(kwargs)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200269 elif kwargs:
270 raise TypeError('macro %r takes no keyword argument %r' %
271 (self.name, iter(kwargs).next()))
272 if self.catch_varargs:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200273 arguments.append(args[self._argument_count:])
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200274 elif len(args) > self._argument_count:
275 raise TypeError('macro %r takes not more than %d argument(s)' %
276 (self.name, len(self.arguments)))
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200277 return self._func(*arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200278
279 def __repr__(self):
280 return '<%s %s>' % (
281 self.__class__.__name__,
282 self.name is None and 'anonymous' or repr(self.name)
283 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200284
285
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200286def fail_with_undefined_error(self, *args, **kwargs):
287 """Regular callback function for undefined objects that raises an
288 `UndefinedError` on call.
289 """
290 if self._undefined_hint is None:
291 if self._undefined_obj is None:
292 hint = '%r is undefined' % self._undefined_name
293 elif not isinstance(self._undefined_name, basestring):
294 hint = '%r object has no element %r' % (
295 self._undefined_obj.__class__.__name__,
296 self._undefined_name
297 )
298 else:
299 hint = '%r object has no attribute %r' % (
300 self._undefined_obj.__class__.__name__,
301 self._undefined_name
302 )
303 else:
304 hint = self._undefined_hint
305 raise UndefinedError(hint)
306
307
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200308class Undefined(object):
Armin Ronacherd1342312008-04-28 12:20:12 +0200309 """The default undefined type. This undefined type can be printed and
310 iterated over, but every other access will raise an :exc:`UndefinedError`:
311
312 >>> foo = Undefined(name='foo')
313 >>> str(foo)
314 ''
315 >>> not foo
316 True
317 >>> foo + 42
318 Traceback (most recent call last):
319 ...
320 jinja2.exceptions.UndefinedError: 'foo' is undefined
Armin Ronacherc63243e2008-04-14 22:53:58 +0200321 """
Armin Ronacher53042292008-04-26 18:30:19 +0200322 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200323
Armin Ronacher9a822052008-04-17 18:44:07 +0200324 def __init__(self, hint=None, obj=None, name=None):
325 self._undefined_hint = hint
326 self._undefined_obj = obj
327 self._undefined_name = name
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200328
Armin Ronacherc63243e2008-04-14 22:53:58 +0200329 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
330 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
331 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher53042292008-04-26 18:30:19 +0200332 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
333 fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200334
335 def __str__(self):
336 return self.__unicode__().encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200337
338 def __repr__(self):
Priit Laes4149a0e2008-04-17 19:04:44 +0200339 return 'Undefined'
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200340
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200341 def __unicode__(self):
342 return u''
343
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200344 def __len__(self):
345 return 0
346
347 def __iter__(self):
348 if 0:
349 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200350
351 def __nonzero__(self):
352 return False
353
354
355class DebugUndefined(Undefined):
Armin Ronacherd1342312008-04-28 12:20:12 +0200356 """An undefined that returns the debug info when printed.
357
358 >>> foo = DebugUndefined(name='foo')
359 >>> str(foo)
360 '{{ foo }}'
361 >>> not foo
362 True
363 >>> foo + 42
364 Traceback (most recent call last):
365 ...
366 jinja2.exceptions.UndefinedError: 'foo' is undefined
367 """
Armin Ronacher53042292008-04-26 18:30:19 +0200368 __slots__ = ()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200369
370 def __unicode__(self):
Armin Ronacher9a822052008-04-17 18:44:07 +0200371 if self._undefined_hint is None:
372 if self._undefined_obj is None:
373 return u'{{ %s }}' % self._undefined_name
374 return '{{ no such element: %s[%r] }}' % (
375 self._undefined_obj.__class__.__name__,
376 self._undefined_name
377 )
378 return u'{{ undefined value printed: %s }}' % self._undefined_hint
Armin Ronacherc63243e2008-04-14 22:53:58 +0200379
380
381class StrictUndefined(Undefined):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200382 """An undefined that barks on print and iteration as well as boolean
Armin Ronacher53042292008-04-26 18:30:19 +0200383 tests and all kinds of comparisons. In other words: you can do nothing
384 with it except checking if it's defined using the `defined` test.
Armin Ronacherd1342312008-04-28 12:20:12 +0200385
386 >>> foo = StrictUndefined(name='foo')
387 >>> str(foo)
388 Traceback (most recent call last):
389 ...
390 jinja2.exceptions.UndefinedError: 'foo' is undefined
391 >>> not foo
392 Traceback (most recent call last):
393 ...
394 jinja2.exceptions.UndefinedError: 'foo' is undefined
395 >>> foo + 42
396 Traceback (most recent call last):
397 ...
398 jinja2.exceptions.UndefinedError: 'foo' is undefined
Priit Laes4149a0e2008-04-17 19:04:44 +0200399 """
Armin Ronacher53042292008-04-26 18:30:19 +0200400 __slots__ = ()
401 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
402 fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200403
Armin Ronacher53042292008-04-26 18:30:19 +0200404
405# remove remaining slots attributes, after the metaclass did the magic they
406# are unneeded and irritating as they contain wrong data for the subclasses.
407del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__