blob: 8cc1b2ffa1f8948b3b178be49deb5b5475d8870d [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"""
11try:
12 from collections import defaultdict
13except ImportError:
14 defaultdict = None
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020015from types import FunctionType
16from jinja2.utils import Markup, partial
Armin Ronacher9a822052008-04-17 18:44:07 +020017from jinja2.exceptions import UndefinedError
Armin Ronachere791c2a2008-04-07 18:39:54 +020018
19
Armin Ronacherc63243e2008-04-14 22:53:58 +020020__all__ = ['LoopContext', 'StaticLoopContext', 'TemplateContext',
Armin Ronacher5236d8c2008-04-17 11:23:16 +020021 'Macro', 'IncludedTemplate', 'Markup']
Armin Ronacher8edbe492008-04-10 20:43:43 +020022
23
Armin Ronachere791c2a2008-04-07 18:39:54 +020024class TemplateContext(dict):
Armin Ronacher8edbe492008-04-10 20:43:43 +020025 """Holds the variables of the local template or of the global one. It's
Armin Ronacher9706fab2008-04-08 18:49:56 +020026 not save to use this class outside of the compiled code. For example
27 update and other methods will not work as they seem (they don't update
28 the exported variables for example).
29 """
Armin Ronachere791c2a2008-04-07 18:39:54 +020030
Armin Ronacher68f77672008-04-17 11:50:39 +020031 def __init__(self, environment, globals, name, blocks, standalone):
Armin Ronacher9706fab2008-04-08 18:49:56 +020032 dict.__init__(self, globals)
Armin Ronacherc63243e2008-04-14 22:53:58 +020033 self.environment = environment
Armin Ronacher9706fab2008-04-08 18:49:56 +020034 self.exported = set()
Armin Ronacher68f77672008-04-17 11:50:39 +020035 self.name = name
Armin Ronacher75cfb862008-04-11 13:47:22 +020036 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
Armin Ronacherf059ec12008-04-11 22:21:00 +020037
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020038 # give all context functions the context as first argument
39 for key, value in self.iteritems():
40 if type(value) is FunctionType and \
41 getattr(value, 'contextfunction', False):
42 dict.__setitem__(self, key, partial(value, self))
43
Armin Ronacherf059ec12008-04-11 22:21:00 +020044 # if the template is in standalone mode we don't copy the blocks over.
45 # this is used for includes for example but otherwise, if the globals
46 # are a template context, this template is participating in a template
47 # inheritance chain and we have to copy the blocks over.
48 if not standalone and isinstance(globals, TemplateContext):
Armin Ronacher62f8a292008-04-13 23:18:05 +020049 for name, parent_blocks in globals.blocks.iteritems():
50 self.blocks.setdefault(name, []).extend(parent_blocks)
51
52 def super(self, block):
53 """Render a parent block."""
54 try:
55 func = self.blocks[block][-2]
56 except LookupError:
Armin Ronacher9a822052008-04-17 18:44:07 +020057 return self.environment.undefined('there is no parent block '
58 'called %r.' % block)
Armin Ronacher62f8a292008-04-13 23:18:05 +020059 return SuperBlock(block, self, func)
Armin Ronachere791c2a2008-04-07 18:39:54 +020060
Armin Ronacher9706fab2008-04-08 18:49:56 +020061 def __setitem__(self, key, value):
62 """If we set items to the dict we track the variables set so
63 that includes can access the exported variables."""
64 dict.__setitem__(self, key, value)
65 self.exported.add(key)
66
Armin Ronacher9706fab2008-04-08 18:49:56 +020067 def get_exported(self):
68 """Get a dict of all exported variables."""
69 return dict((k, self[k]) for k in self.exported)
70
Armin Ronachere791c2a2008-04-07 18:39:54 +020071 # if there is a default dict, dict has a __missing__ method we can use.
72 if defaultdict is None:
73 def __getitem__(self, name):
74 if name in self:
75 return self[name]
Armin Ronacher9a822052008-04-17 18:44:07 +020076 return self.environment.undefined(name=name)
Armin Ronachere791c2a2008-04-07 18:39:54 +020077 else:
Armin Ronacher9a822052008-04-17 18:44:07 +020078 def __missing__(self, name):
79 return self.environment.undefined(name=name)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020080
Armin Ronacherf059ec12008-04-11 22:21:00 +020081 def __repr__(self):
82 return '<%s %s of %r>' % (
83 self.__class__.__name__,
84 dict.__repr__(self),
Armin Ronacher68f77672008-04-17 11:50:39 +020085 self.name
Armin Ronacherf059ec12008-04-11 22:21:00 +020086 )
87
88
Armin Ronacher62f8a292008-04-13 23:18:05 +020089class SuperBlock(object):
90 """When called this renders a parent block."""
91
92 def __init__(self, name, context, render_func):
93 self.name = name
94 self._context = context
95 self._render_func = render_func
96
97 def __call__(self):
Armin Ronacher5236d8c2008-04-17 11:23:16 +020098 return Markup(u''.join(self._render_func(self._context)))
Armin Ronacher62f8a292008-04-13 23:18:05 +020099
100 def __repr__(self):
101 return '<%s %r>' % (
102 self.__class__.__name__,
103 self.name
104 )
105
106
Armin Ronacherf059ec12008-04-11 22:21:00 +0200107class IncludedTemplate(object):
108 """Represents an included template."""
109
110 def __init__(self, environment, context, template):
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200111 template = environment.get_template(template)
112 gen = template.root_render_func(context, standalone=True)
113 context = gen.next()
Armin Ronacher68f77672008-04-17 11:50:39 +0200114 self._name = template.name
Armin Ronacherf059ec12008-04-11 22:21:00 +0200115 self._rendered_body = u''.join(gen)
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200116 self._context = context.get_exported()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200117
Armin Ronacherf59bac22008-04-20 13:11:43 +0200118 __getitem__ = lambda x, n: x._context[n]
119 __html__ = __unicode__ = lambda x: x._rendered_body
Armin Ronacherf059ec12008-04-11 22:21:00 +0200120
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200121 def __repr__(self):
122 return '<%s %r>' % (
123 self.__class__.__name__,
Armin Ronacher68f77672008-04-17 11:50:39 +0200124 self._name
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200125 )
126
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200127
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200128class LoopContextBase(object):
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200129 """Helper for extended iteration."""
130
131 def __init__(self, iterable, parent=None):
132 self._iterable = iterable
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200133 self._length = None
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200134 self.index0 = 0
135 self.parent = parent
136
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200137 def cycle(self, *args):
138 """A replacement for the old ``{% cycle %}`` tag."""
139 if not args:
140 raise TypeError('no items for cycling given')
141 return args[self.index0 % len(args)]
142
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200143 first = property(lambda x: x.index0 == 0)
144 last = property(lambda x: x.revindex0 == 0)
145 index = property(lambda x: x.index0 + 1)
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200146 revindex = property(lambda x: x.length - x.index0)
147 revindex0 = property(lambda x: x.length - x.index)
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200148
149 def __len__(self):
150 return self.length
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200151
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200152
153class LoopContext(LoopContextBase):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200154 """A loop context for dynamic iteration."""
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200155
Armin Ronacherf64efb82008-04-18 10:32:14 +0200156 def __init__(self, iterable, enforce_length=False):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200157 self._iterable = iterable
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200158 self._next = iter(iterable).next
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200159 self._length = None
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200160 self.index0 = -1
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200161 if enforce_length:
162 len(self)
163
164 def make_static(self):
165 """Return a static loop context for the optimizer."""
Armin Ronacherf64efb82008-04-18 10:32:14 +0200166 return StaticLoopContext(self.index0, self.length)
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200167
168 def __iter__(self):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200169 return self
170
171 def next(self):
172 self.index0 += 1
173 return self._next(), self
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200174
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200175 @property
176 def length(self):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200177 if self._length is None:
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200178 try:
179 length = len(self._iterable)
180 except TypeError:
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200181 self._iterable = tuple(self._iterable)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200182 self._next = iter(self._iterable).next
183 length = len(tuple(self._iterable)) + self.index0 + 1
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200184 self._length = length
185 return self._length
186
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200187 def __repr__(self):
188 return 'LoopContext(%r)' % self.index0
189
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200190
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200191class StaticLoopContext(LoopContextBase):
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200192 """The static loop context is used in the optimizer to "freeze" the
193 status of an iteration. The only reason for this object is if the
194 loop object is accessed in a non static way (eg: becomes part of a
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200195 function call).
196 """
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200197
Armin Ronacherf64efb82008-04-18 10:32:14 +0200198 def __init__(self, index0, length):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200199 self.index0 = index0
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200200 self.length = length
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200201
202 def __repr__(self):
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200203 """The repr is used by the optimizer to dump the object."""
Armin Ronacherf64efb82008-04-18 10:32:14 +0200204 return 'StaticLoopContext(%r, %r)' % (
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200205 self.index0,
Armin Ronacherf64efb82008-04-18 10:32:14 +0200206 self.length
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200207 )
208
209 def make_static(self):
210 return self
211
212
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200213class Macro(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200214 """Wraps a macro."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200215
Armin Ronacherc63243e2008-04-14 22:53:58 +0200216 def __init__(self, environment, func, name, arguments, defaults, catch_all, caller):
217 self._environment = environment
Armin Ronacher71082072008-04-12 14:19:36 +0200218 self._func = func
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200219 self.name = name
220 self.arguments = arguments
221 self.defaults = defaults
222 self.catch_all = catch_all
Armin Ronacher71082072008-04-12 14:19:36 +0200223 self.caller = caller
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200224
225 def __call__(self, *args, **kwargs):
Armin Ronacher9706fab2008-04-08 18:49:56 +0200226 arg_count = len(self.arguments)
227 if len(args) > arg_count:
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200228 raise TypeError('macro %r takes not more than %d argument(s).' %
229 (self.name, len(self.arguments)))
230 arguments = {}
Armin Ronacher9706fab2008-04-08 18:49:56 +0200231 for idx, name in enumerate(self.arguments):
232 try:
233 value = args[idx]
234 except IndexError:
235 try:
236 value = kwargs.pop(name)
237 except KeyError:
238 try:
239 value = self.defaults[idx - arg_count]
240 except IndexError:
Armin Ronacher9a822052008-04-17 18:44:07 +0200241 value = self._environment.undefined(
242 'parameter %r was not provided' % name)
Christoph Hackf9f029c2008-04-09 15:08:11 +0200243 arguments['l_' + name] = value
Armin Ronacher71082072008-04-12 14:19:36 +0200244 if self.caller:
245 caller = kwargs.pop('caller', None)
246 if caller is None:
Armin Ronacher9a822052008-04-17 18:44:07 +0200247 caller = self._environment.undefined('No caller defined')
Armin Ronacher71082072008-04-12 14:19:36 +0200248 arguments['l_caller'] = caller
Armin Ronacher9706fab2008-04-08 18:49:56 +0200249 if self.catch_all:
250 arguments['l_arguments'] = kwargs
Armin Ronacher625215e2008-04-13 16:31:08 +0200251 return self._func(**arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200252
253 def __repr__(self):
254 return '<%s %s>' % (
255 self.__class__.__name__,
256 self.name is None and 'anonymous' or repr(self.name)
257 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200258
259
260class Undefined(object):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200261 """The default undefined implementation. This undefined implementation
262 can be printed and iterated over, but every other access will raise a
263 `NameError`. Custom undefined classes must subclass this.
264 """
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200265
Armin Ronacher9a822052008-04-17 18:44:07 +0200266 def __init__(self, hint=None, obj=None, name=None):
267 self._undefined_hint = hint
268 self._undefined_obj = obj
269 self._undefined_name = name
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200270
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200271 def _fail_with_error(self, *args, **kwargs):
Armin Ronacher9a822052008-04-17 18:44:07 +0200272 if self._undefined_hint is None:
273 if self._undefined_obj is None:
274 hint = '%r is undefined' % self._undefined_name
275 elif not isinstance(self._undefined_name, basestring):
276 hint = '%r object has no element %r' % (
277 self._undefined_obj.__class__.__name__,
278 self._undefined_name
279 )
280 else:
Armin Ronacherf41d1392008-04-18 16:41:52 +0200281 hint = '%r object has no attribute %r' % (
Armin Ronacher9a822052008-04-17 18:44:07 +0200282 self._undefined_obj.__class__.__name__,
283 self._undefined_name
284 )
285 else:
286 hint = self._undefined_hint
287 raise UndefinedError(hint)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200288 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
289 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
290 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200291 __getattr__ = __getitem__ = _fail_with_error
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200292
293 def __unicode__(self):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200294 return u''
295
296 def __str__(self):
297 return self.__unicode__().encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200298
299 def __repr__(self):
Priit Laes4149a0e2008-04-17 19:04:44 +0200300 return 'Undefined'
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200301
302 def __len__(self):
303 return 0
304
305 def __iter__(self):
306 if 0:
307 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200308
309 def __nonzero__(self):
310 return False
311
312
313class DebugUndefined(Undefined):
314 """An undefined that returns the debug info when printed."""
315
316 def __unicode__(self):
Armin Ronacher9a822052008-04-17 18:44:07 +0200317 if self._undefined_hint is None:
318 if self._undefined_obj is None:
319 return u'{{ %s }}' % self._undefined_name
320 return '{{ no such element: %s[%r] }}' % (
321 self._undefined_obj.__class__.__name__,
322 self._undefined_name
323 )
324 return u'{{ undefined value printed: %s }}' % self._undefined_hint
Armin Ronacherc63243e2008-04-14 22:53:58 +0200325
326
327class StrictUndefined(Undefined):
Priit Laes4149a0e2008-04-17 19:04:44 +0200328 """An undefined that barks on print and iteration as well as boolean tests.
329 In other words: you can do nothing with it except checking if it's defined
330 using the `defined` test.
331 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200332
Priit Laes4149a0e2008-04-17 19:04:44 +0200333 __iter__ = __unicode__ = __len__ = __nonzero__ = Undefined._fail_with_error