blob: f4a2f1aceb19aa34f637fab3052c7cf254fc6045 [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
14from jinja2.utils import Markup, partial, soft_unicode, escape
15from 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 Ronacherc9705c22008-04-27 21:28:03 +020019__all__ = ['LoopContext', 'TemplateContext', 'TemplateReference', 'Macro',
Armin Ronacherd1342312008-04-28 12:20:12 +020020 'TemplateRuntimeError', 'Markup', 'missing', 'concat', 'escape',
21 'markup_join', 'unicode_join']
Armin Ronacher0611e492008-04-25 23:44:14 +020022
23
24# special singleton representing missing values for the runtime
Armin Ronacher32a910f2008-04-26 23:21:03 +020025missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
Armin Ronacher8edbe492008-04-10 20:43:43 +020026
27
Armin Ronacherde6bf712008-04-26 01:44:14 +020028# concatenate a list of strings and convert them to unicode.
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020029# unfortunately there is a bug in python 2.4 and lower that causes
30# unicode.join trash the traceback.
31try:
32 def _test_gen_bug():
33 raise TypeError(_test_gen_bug)
34 yield None
35 u''.join(_test_gen_bug())
36except TypeError, e:
37 if e.args and e.args[0] is _test_gen_bug:
38 concat = u''.join
39 else:
40 def concat(gen):
41 try:
Armin Ronacherd84ec462008-04-29 13:43:16 +020042 return u''.join(list(gen))
Armin Ronacher1ae4fdf2008-04-28 20:49:51 +020043 except:
44 exc_type, exc_value, tb = sys.exc_info()
45 raise exc_type, exc_value, tb.tb_next
46del _test_gen_bug
Armin Ronacherde6bf712008-04-26 01:44:14 +020047
48
Armin Ronacherd1342312008-04-28 12:20:12 +020049def markup_join(*args):
50 """Concatenation that escapes if necessary and converts to unicode."""
51 buf = []
52 iterator = imap(soft_unicode, args)
53 for arg in iterator:
54 buf.append(arg)
55 if hasattr(arg, '__html__'):
56 return Markup(u'').join(chain(buf, iterator))
57 return concat(buf)
58
59
60def unicode_join(*args):
61 """Simple args to unicode conversion and concatenation."""
62 return concat(imap(unicode, args))
63
64
Armin Ronacher203bfcb2008-04-24 21:54:44 +020065class TemplateContext(object):
Armin Ronacher8edbe492008-04-10 20:43:43 +020066 """Holds the variables of the local template or of the global one. It's
Armin Ronacher9706fab2008-04-08 18:49:56 +020067 not save to use this class outside of the compiled code. For example
68 update and other methods will not work as they seem (they don't update
69 the exported variables for example).
Armin Ronacherc9705c22008-04-27 21:28:03 +020070
71 The context is immutable. Modifications on `parent` must not happen and
72 modifications on `vars` are allowed from generated template code. However
73 functions that are passed the template context may not modify the context
74 in any way.
Armin Ronacher9706fab2008-04-08 18:49:56 +020075 """
Armin Ronachere791c2a2008-04-07 18:39:54 +020076
Armin Ronacher203bfcb2008-04-24 21:54:44 +020077 def __init__(self, environment, parent, name, blocks):
78 self.parent = parent
Armin Ronacher2feed1d2008-04-26 16:26:52 +020079 self.vars = vars = {}
Armin Ronacherc63243e2008-04-14 22:53:58 +020080 self.environment = environment
Armin Ronacher203bfcb2008-04-24 21:54:44 +020081 self.exported_vars = set()
Armin Ronacher68f77672008-04-17 11:50:39 +020082 self.name = name
Armin Ronacher203bfcb2008-04-24 21:54:44 +020083
84 # bind functions to the context of environment if required
Armin Ronacher2feed1d2008-04-26 16:26:52 +020085 for name, obj in parent.iteritems():
Armin Ronacher203bfcb2008-04-24 21:54:44 +020086 if type(obj) is FunctionType:
87 if getattr(obj, 'contextfunction', 0):
Armin Ronacher2feed1d2008-04-26 16:26:52 +020088 vars[name] = partial(obj, self)
Armin Ronacher203bfcb2008-04-24 21:54:44 +020089 elif getattr(obj, 'environmentfunction', 0):
Armin Ronacher2feed1d2008-04-26 16:26:52 +020090 vars[name] = partial(obj, environment)
Armin Ronacher203bfcb2008-04-24 21:54:44 +020091
92 # create the initial mapping of blocks. Whenever template inheritance
93 # takes place the runtime will update this mapping with the new blocks
94 # from the template.
Armin Ronacher75cfb862008-04-11 13:47:22 +020095 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
Armin Ronacherf059ec12008-04-11 22:21:00 +020096
Armin Ronacher203bfcb2008-04-24 21:54:44 +020097 def super(self, name, current):
Armin Ronacher62f8a292008-04-13 23:18:05 +020098 """Render a parent block."""
Armin Ronacherc9705c22008-04-27 21:28:03 +020099 try:
100 blocks = self.blocks[name]
101 pos = blocks.index(current) - 1
102 if pos < 0:
103 raise IndexError()
104 except LookupError:
Armin Ronacher9a822052008-04-17 18:44:07 +0200105 return self.environment.undefined('there is no parent block '
Armin Ronacherc9705c22008-04-27 21:28:03 +0200106 'called %r.' % name)
Armin Ronacherd1342312008-04-28 12:20:12 +0200107 wrap = self.environment.autoescape and Markup or (lambda x: x)
108 render = lambda: wrap(concat(blocks[pos](self)))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200109 render.__name__ = render.name = name
110 return render
Armin Ronachere791c2a2008-04-07 18:39:54 +0200111
Armin Ronacher53042292008-04-26 18:30:19 +0200112 def get(self, key, default=None):
Armin Ronacherb5124e62008-04-25 00:36:14 +0200113 """For dict compatibility"""
Armin Ronacher53042292008-04-26 18:30:19 +0200114 if key in self.vars:
115 return self.vars[key]
116 if key in self.parent:
117 return self.parent[key]
118 return default
Armin Ronacherb5124e62008-04-25 00:36:14 +0200119
Armin Ronacher9706fab2008-04-08 18:49:56 +0200120 def get_exported(self):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200121 """Get a new dict with the exported variables."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200122 return dict((k, self.vars[k]) for k in self.exported_vars)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200123
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200124 def get_root(self):
125 """Return a new dict with all the non local variables."""
126 return dict(self.parent)
127
128 def get_all(self):
129 """Return a copy of the complete context as dict."""
130 return dict(self.parent, **self.vars)
131
Armin Ronacherc9705c22008-04-27 21:28:03 +0200132 def clone(self):
133 """Return a copy of the context without the locals."""
134 return self.__class__(self.environment, self.parent,
135 self.name, self.blocks)
136
Armin Ronacherb5124e62008-04-25 00:36:14 +0200137 def __contains__(self, name):
138 return name in self.vars or name in self.parent
139
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200140 def __getitem__(self, key):
141 if key in self.vars:
142 return self.vars[key]
Armin Ronacher53042292008-04-26 18:30:19 +0200143 if key in self.parent:
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200144 return self.parent[key]
Armin Ronacher53042292008-04-26 18:30:19 +0200145 return self.environment.undefined(name=key)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200146
Armin Ronacherf059ec12008-04-11 22:21:00 +0200147 def __repr__(self):
148 return '<%s %s of %r>' % (
149 self.__class__.__name__,
Armin Ronacher963f97d2008-04-25 11:44:59 +0200150 repr(self.get_all()),
Armin Ronacher68f77672008-04-17 11:50:39 +0200151 self.name
Armin Ronacherf059ec12008-04-11 22:21:00 +0200152 )
153
154
Armin Ronacherc9705c22008-04-27 21:28:03 +0200155class TemplateReference(object):
156 """The `self` in templates."""
Armin Ronacher62f8a292008-04-13 23:18:05 +0200157
Armin Ronacherc9705c22008-04-27 21:28:03 +0200158 def __init__(self, context):
159 self.__context = context
Armin Ronacher62f8a292008-04-13 23:18:05 +0200160
Armin Ronacherc9705c22008-04-27 21:28:03 +0200161 def __getitem__(self, name):
162 func = self.__context.blocks[name][-1]
Armin Ronacherd1342312008-04-28 12:20:12 +0200163 wrap = self.__context.environment.autoescape and \
164 Markup or (lambda x: x)
165 render = lambda: wrap(concat(func(self.__context)))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200166 render.__name__ = render.name = name
167 return render
Armin Ronacher62f8a292008-04-13 23:18:05 +0200168
169 def __repr__(self):
170 return '<%s %r>' % (
171 self.__class__.__name__,
Armin Ronacherc9705c22008-04-27 21:28:03 +0200172 self._context.name
Armin Ronacher62f8a292008-04-13 23:18:05 +0200173 )
174
175
Armin Ronacher32a910f2008-04-26 23:21:03 +0200176class LoopContext(object):
177 """A loop context for dynamic iteration."""
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200178
Armin Ronacher32a910f2008-04-26 23:21:03 +0200179 def __init__(self, iterable, enforce_length=False):
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200180 self._iterable = iterable
Armin Ronacher32a910f2008-04-26 23:21:03 +0200181 self._next = iter(iterable).next
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200182 self._length = None
Armin Ronacher32a910f2008-04-26 23:21:03 +0200183 self.index0 = -1
184 if enforce_length:
185 len(self)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200186
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200187 def cycle(self, *args):
188 """A replacement for the old ``{% cycle %}`` tag."""
189 if not args:
190 raise TypeError('no items for cycling given')
191 return args[self.index0 % len(args)]
192
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200193 first = property(lambda x: x.index0 == 0)
194 last = property(lambda x: x.revindex0 == 0)
195 index = property(lambda x: x.index0 + 1)
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200196 revindex = property(lambda x: x.length - x.index0)
197 revindex0 = property(lambda x: x.length - x.index)
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200198
199 def __len__(self):
200 return self.length
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200201
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200202 def __iter__(self):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200203 return self
204
205 def next(self):
206 self.index0 += 1
207 return self._next(), self
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200208
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200209 @property
210 def length(self):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200211 if self._length is None:
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200212 try:
213 length = len(self._iterable)
214 except TypeError:
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200215 self._iterable = tuple(self._iterable)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200216 self._next = iter(self._iterable).next
217 length = len(tuple(self._iterable)) + self.index0 + 1
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200218 self._length = length
219 return self._length
220
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200221 def __repr__(self):
Armin Ronacherc9705c22008-04-27 21:28:03 +0200222 return '<%s %r/%r>' % (
223 self.__class__.__name__,
224 self.index,
225 self.length
226 )
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200227
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200228
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200229class Macro(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200230 """Wraps a macro."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200231
Armin Ronacher963f97d2008-04-25 11:44:59 +0200232 def __init__(self, environment, func, name, arguments, defaults,
233 catch_kwargs, catch_varargs, caller):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200234 self._environment = environment
Armin Ronacher71082072008-04-12 14:19:36 +0200235 self._func = func
Armin Ronacherd84ec462008-04-29 13:43:16 +0200236 self._argument_count = len(arguments)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200237 self.name = name
238 self.arguments = arguments
239 self.defaults = defaults
Armin Ronacher963f97d2008-04-25 11:44:59 +0200240 self.catch_kwargs = catch_kwargs
241 self.catch_varargs = catch_varargs
Armin Ronacher71082072008-04-12 14:19:36 +0200242 self.caller = caller
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200243
244 def __call__(self, *args, **kwargs):
Armin Ronacherd84ec462008-04-29 13:43:16 +0200245 if not self.catch_varargs and len(args) > self._argument_count:
Armin Ronacher963f97d2008-04-25 11:44:59 +0200246 raise TypeError('macro %r takes not more than %d argument(s)' %
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200247 (self.name, len(self.arguments)))
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200248 arguments = []
Armin Ronacher9706fab2008-04-08 18:49:56 +0200249 for idx, name in enumerate(self.arguments):
250 try:
251 value = args[idx]
252 except IndexError:
253 try:
254 value = kwargs.pop(name)
255 except KeyError:
256 try:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200257 value = self.defaults[idx - self._argument_count]
Armin Ronacher9706fab2008-04-08 18:49:56 +0200258 except IndexError:
Armin Ronacher9a822052008-04-17 18:44:07 +0200259 value = self._environment.undefined(
260 'parameter %r was not provided' % name)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200261 arguments.append(value)
262
263 # it's important that the order of these arguments does not change
264 # if not also changed in the compiler's `function_scoping` method.
265 # the order is caller, keyword arguments, positional arguments!
Armin Ronacher71082072008-04-12 14:19:36 +0200266 if self.caller:
267 caller = kwargs.pop('caller', None)
268 if caller is None:
Armin Ronacher9a822052008-04-17 18:44:07 +0200269 caller = self._environment.undefined('No caller defined')
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200270 arguments.append(caller)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200271 if self.catch_kwargs:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200272 arguments.append(kwargs)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200273 elif kwargs:
274 raise TypeError('macro %r takes no keyword argument %r' %
275 (self.name, iter(kwargs).next()))
276 if self.catch_varargs:
Armin Ronacherd84ec462008-04-29 13:43:16 +0200277 arguments.append(args[self._argument_count:])
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200278 return self._func(*arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200279
280 def __repr__(self):
281 return '<%s %s>' % (
282 self.__class__.__name__,
283 self.name is None and 'anonymous' or repr(self.name)
284 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200285
286
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200287def fail_with_undefined_error(self, *args, **kwargs):
288 """Regular callback function for undefined objects that raises an
289 `UndefinedError` on call.
290 """
291 if self._undefined_hint is None:
292 if self._undefined_obj is None:
293 hint = '%r is undefined' % self._undefined_name
294 elif not isinstance(self._undefined_name, basestring):
295 hint = '%r object has no element %r' % (
296 self._undefined_obj.__class__.__name__,
297 self._undefined_name
298 )
299 else:
300 hint = '%r object has no attribute %r' % (
301 self._undefined_obj.__class__.__name__,
302 self._undefined_name
303 )
304 else:
305 hint = self._undefined_hint
306 raise UndefinedError(hint)
307
308
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200309class Undefined(object):
Armin Ronacherd1342312008-04-28 12:20:12 +0200310 """The default undefined type. This undefined type can be printed and
311 iterated over, but every other access will raise an :exc:`UndefinedError`:
312
313 >>> foo = Undefined(name='foo')
314 >>> str(foo)
315 ''
316 >>> not foo
317 True
318 >>> foo + 42
319 Traceback (most recent call last):
320 ...
321 jinja2.exceptions.UndefinedError: 'foo' is undefined
Armin Ronacherc63243e2008-04-14 22:53:58 +0200322 """
Armin Ronacher53042292008-04-26 18:30:19 +0200323 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200324
Armin Ronacher9a822052008-04-17 18:44:07 +0200325 def __init__(self, hint=None, obj=None, name=None):
326 self._undefined_hint = hint
327 self._undefined_obj = obj
328 self._undefined_name = name
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200329
Armin Ronacherc63243e2008-04-14 22:53:58 +0200330 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
331 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
332 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher53042292008-04-26 18:30:19 +0200333 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
334 fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200335
336 def __str__(self):
337 return self.__unicode__().encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200338
339 def __repr__(self):
Priit Laes4149a0e2008-04-17 19:04:44 +0200340 return 'Undefined'
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200341
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200342 def __unicode__(self):
343 return u''
344
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200345 def __len__(self):
346 return 0
347
348 def __iter__(self):
349 if 0:
350 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200351
352 def __nonzero__(self):
353 return False
354
355
356class DebugUndefined(Undefined):
Armin Ronacherd1342312008-04-28 12:20:12 +0200357 """An undefined that returns the debug info when printed.
358
359 >>> foo = DebugUndefined(name='foo')
360 >>> str(foo)
361 '{{ foo }}'
362 >>> not foo
363 True
364 >>> foo + 42
365 Traceback (most recent call last):
366 ...
367 jinja2.exceptions.UndefinedError: 'foo' is undefined
368 """
Armin Ronacher53042292008-04-26 18:30:19 +0200369 __slots__ = ()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200370
371 def __unicode__(self):
Armin Ronacher9a822052008-04-17 18:44:07 +0200372 if self._undefined_hint is None:
373 if self._undefined_obj is None:
374 return u'{{ %s }}' % self._undefined_name
375 return '{{ no such element: %s[%r] }}' % (
376 self._undefined_obj.__class__.__name__,
377 self._undefined_name
378 )
379 return u'{{ undefined value printed: %s }}' % self._undefined_hint
Armin Ronacherc63243e2008-04-14 22:53:58 +0200380
381
382class StrictUndefined(Undefined):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200383 """An undefined that barks on print and iteration as well as boolean
Armin Ronacher53042292008-04-26 18:30:19 +0200384 tests and all kinds of comparisons. In other words: you can do nothing
385 with it except checking if it's defined using the `defined` test.
Armin Ronacherd1342312008-04-28 12:20:12 +0200386
387 >>> foo = StrictUndefined(name='foo')
388 >>> str(foo)
389 Traceback (most recent call last):
390 ...
391 jinja2.exceptions.UndefinedError: 'foo' is undefined
392 >>> not foo
393 Traceback (most recent call last):
394 ...
395 jinja2.exceptions.UndefinedError: 'foo' is undefined
396 >>> foo + 42
397 Traceback (most recent call last):
398 ...
399 jinja2.exceptions.UndefinedError: 'foo' is undefined
Priit Laes4149a0e2008-04-17 19:04:44 +0200400 """
Armin Ronacher53042292008-04-26 18:30:19 +0200401 __slots__ = ()
402 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
403 fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200404
Armin Ronacher53042292008-04-26 18:30:19 +0200405
406# remove remaining slots attributes, after the metaclass did the magic they
407# are unneeded and irritating as they contain wrong data for the subclasses.
408del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__