blob: 0aad4bdb3e5e9b6d299ca2b7acb47aca4e173796 [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:
42 return u''.join(list(gen()))
43 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 Ronacher4f62a9f2008-04-08 18:09:13 +0200236 self.name = name
237 self.arguments = arguments
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200238 self.argument_count = len(arguments)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200239 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 Ronacher2feed1d2008-04-26 16:26:52 +0200245 self.argument_count = len(self.arguments)
246 if not self.catch_varargs and len(args) > self.argument_count:
Armin Ronacher963f97d2008-04-25 11:44:59 +0200247 raise TypeError('macro %r takes not more than %d argument(s)' %
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200248 (self.name, len(self.arguments)))
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200249 arguments = []
Armin Ronacher9706fab2008-04-08 18:49:56 +0200250 for idx, name in enumerate(self.arguments):
251 try:
252 value = args[idx]
253 except IndexError:
254 try:
255 value = kwargs.pop(name)
256 except KeyError:
257 try:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200258 value = self.defaults[idx - self.argument_count]
Armin Ronacher9706fab2008-04-08 18:49:56 +0200259 except IndexError:
Armin Ronacher9a822052008-04-17 18:44:07 +0200260 value = self._environment.undefined(
261 'parameter %r was not provided' % name)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200262 arguments.append(value)
263
264 # it's important that the order of these arguments does not change
265 # if not also changed in the compiler's `function_scoping` method.
266 # the order is caller, keyword arguments, positional arguments!
Armin Ronacher71082072008-04-12 14:19:36 +0200267 if self.caller:
268 caller = kwargs.pop('caller', None)
269 if caller is None:
Armin Ronacher9a822052008-04-17 18:44:07 +0200270 caller = self._environment.undefined('No caller defined')
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200271 arguments.append(caller)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200272 if self.catch_kwargs:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200273 arguments.append(kwargs)
Armin Ronacher963f97d2008-04-25 11:44:59 +0200274 elif kwargs:
275 raise TypeError('macro %r takes no keyword argument %r' %
276 (self.name, iter(kwargs).next()))
277 if self.catch_varargs:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200278 arguments.append(args[self.argument_count:])
279 return self._func(*arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200280
281 def __repr__(self):
282 return '<%s %s>' % (
283 self.__class__.__name__,
284 self.name is None and 'anonymous' or repr(self.name)
285 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200286
287
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200288def fail_with_undefined_error(self, *args, **kwargs):
289 """Regular callback function for undefined objects that raises an
290 `UndefinedError` on call.
291 """
292 if self._undefined_hint is None:
293 if self._undefined_obj is None:
294 hint = '%r is undefined' % self._undefined_name
295 elif not isinstance(self._undefined_name, basestring):
296 hint = '%r object has no element %r' % (
297 self._undefined_obj.__class__.__name__,
298 self._undefined_name
299 )
300 else:
301 hint = '%r object has no attribute %r' % (
302 self._undefined_obj.__class__.__name__,
303 self._undefined_name
304 )
305 else:
306 hint = self._undefined_hint
307 raise UndefinedError(hint)
308
309
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200310class Undefined(object):
Armin Ronacherd1342312008-04-28 12:20:12 +0200311 """The default undefined type. This undefined type can be printed and
312 iterated over, but every other access will raise an :exc:`UndefinedError`:
313
314 >>> foo = Undefined(name='foo')
315 >>> str(foo)
316 ''
317 >>> not foo
318 True
319 >>> foo + 42
320 Traceback (most recent call last):
321 ...
322 jinja2.exceptions.UndefinedError: 'foo' is undefined
Armin Ronacherc63243e2008-04-14 22:53:58 +0200323 """
Armin Ronacher53042292008-04-26 18:30:19 +0200324 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200325
Armin Ronacher9a822052008-04-17 18:44:07 +0200326 def __init__(self, hint=None, obj=None, name=None):
327 self._undefined_hint = hint
328 self._undefined_obj = obj
329 self._undefined_name = name
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200330
Armin Ronacherc63243e2008-04-14 22:53:58 +0200331 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
332 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
333 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher53042292008-04-26 18:30:19 +0200334 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
335 fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200336
337 def __str__(self):
338 return self.__unicode__().encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200339
340 def __repr__(self):
Priit Laes4149a0e2008-04-17 19:04:44 +0200341 return 'Undefined'
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200342
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200343 def __unicode__(self):
344 return u''
345
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200346 def __len__(self):
347 return 0
348
349 def __iter__(self):
350 if 0:
351 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200352
353 def __nonzero__(self):
354 return False
355
356
357class DebugUndefined(Undefined):
Armin Ronacherd1342312008-04-28 12:20:12 +0200358 """An undefined that returns the debug info when printed.
359
360 >>> foo = DebugUndefined(name='foo')
361 >>> str(foo)
362 '{{ foo }}'
363 >>> not foo
364 True
365 >>> foo + 42
366 Traceback (most recent call last):
367 ...
368 jinja2.exceptions.UndefinedError: 'foo' is undefined
369 """
Armin Ronacher53042292008-04-26 18:30:19 +0200370 __slots__ = ()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200371
372 def __unicode__(self):
Armin Ronacher9a822052008-04-17 18:44:07 +0200373 if self._undefined_hint is None:
374 if self._undefined_obj is None:
375 return u'{{ %s }}' % self._undefined_name
376 return '{{ no such element: %s[%r] }}' % (
377 self._undefined_obj.__class__.__name__,
378 self._undefined_name
379 )
380 return u'{{ undefined value printed: %s }}' % self._undefined_hint
Armin Ronacherc63243e2008-04-14 22:53:58 +0200381
382
383class StrictUndefined(Undefined):
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200384 """An undefined that barks on print and iteration as well as boolean
Armin Ronacher53042292008-04-26 18:30:19 +0200385 tests and all kinds of comparisons. In other words: you can do nothing
386 with it except checking if it's defined using the `defined` test.
Armin Ronacherd1342312008-04-28 12:20:12 +0200387
388 >>> foo = StrictUndefined(name='foo')
389 >>> str(foo)
390 Traceback (most recent call last):
391 ...
392 jinja2.exceptions.UndefinedError: 'foo' is undefined
393 >>> not foo
394 Traceback (most recent call last):
395 ...
396 jinja2.exceptions.UndefinedError: 'foo' is undefined
397 >>> foo + 42
398 Traceback (most recent call last):
399 ...
400 jinja2.exceptions.UndefinedError: 'foo' is undefined
Priit Laes4149a0e2008-04-17 19:04:44 +0200401 """
Armin Ronacher53042292008-04-26 18:30:19 +0200402 __slots__ = ()
403 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
404 fail_with_undefined_error
Armin Ronacherc63243e2008-04-14 22:53:58 +0200405
Armin Ronacher53042292008-04-26 18:30:19 +0200406
407# remove remaining slots attributes, after the metaclass did the magic they
408# are unneeded and irritating as they contain wrong data for the subclasses.
409del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__