blob: 3e3721da91c55e293cb25a960c7d624739091d5c [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
15
16
Armin Ronacherc63243e2008-04-14 22:53:58 +020017__all__ = ['LoopContext', 'StaticLoopContext', 'TemplateContext',
18 'Macro', 'IncludedTemplate', 'TemplateData']
Armin Ronachere791c2a2008-04-07 18:39:54 +020019
20
Armin Ronacher8edbe492008-04-10 20:43:43 +020021class TemplateData(unicode):
22 """Marks data as "coming from the template". This is used to let the
23 system know that this data is already processed if a finalization is
24 used."""
25
26 def __html__(self):
27 return self
28
29
Armin Ronachere791c2a2008-04-07 18:39:54 +020030class TemplateContext(dict):
Armin Ronacher8edbe492008-04-10 20:43:43 +020031 """Holds the variables of the local template or of the global one. It's
Armin Ronacher9706fab2008-04-08 18:49:56 +020032 not save to use this class outside of the compiled code. For example
33 update and other methods will not work as they seem (they don't update
34 the exported variables for example).
35 """
Armin Ronachere791c2a2008-04-07 18:39:54 +020036
Armin Ronacherc63243e2008-04-14 22:53:58 +020037 def __init__(self, environment, globals, filename, blocks, standalone):
Armin Ronacher9706fab2008-04-08 18:49:56 +020038 dict.__init__(self, globals)
Armin Ronacherc63243e2008-04-14 22:53:58 +020039 self.environment = environment
Armin Ronacher9706fab2008-04-08 18:49:56 +020040 self.exported = set()
Armin Ronachere791c2a2008-04-07 18:39:54 +020041 self.filename = filename
Armin Ronacher75cfb862008-04-11 13:47:22 +020042 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
Armin Ronacherf059ec12008-04-11 22:21:00 +020043
44 # 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 Ronacherc63243e2008-04-14 22:53:58 +020057 return self.environment.undefined('super',
58 extra='there is probably no parent block with this name')
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 Ronacherc63243e2008-04-14 22:53:58 +020076 return self.environment.undefined(name)
Armin Ronachere791c2a2008-04-07 18:39:54 +020077 else:
78 def __missing__(self, key):
Armin Ronacherc63243e2008-04-14 22:53:58 +020079 return self.environment.undefined(key)
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),
85 self.filename
86 )
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):
98 return TemplateData(u''.join(self._render_func(self._context)))
99
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()
114 self._filename = 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
118 def __getitem__(self, name):
119 return self._context[name]
120
121 def __unicode__(self):
122 return self._context
123
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200124 def __repr__(self):
125 return '<%s %r>' % (
126 self.__class__.__name__,
127 self._filename
128 )
129
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200130
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200131class LoopContextBase(object):
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200132 """Helper for extended iteration."""
133
134 def __init__(self, iterable, parent=None):
135 self._iterable = iterable
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200136 self._length = None
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200137 self.index0 = 0
138 self.parent = parent
139
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200140 def cycle(self, *args):
141 """A replacement for the old ``{% cycle %}`` tag."""
142 if not args:
143 raise TypeError('no items for cycling given')
144 return args[self.index0 % len(args)]
145
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200146 first = property(lambda x: x.index0 == 0)
147 last = property(lambda x: x.revindex0 == 0)
148 index = property(lambda x: x.index0 + 1)
149 revindex = property(lambda x: x.length)
150 revindex0 = property(lambda x: x.length - 1)
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200151
152 def __len__(self):
153 return self.length
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200154
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200155
156class LoopContext(LoopContextBase):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200157 """A loop context for dynamic iteration."""
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200158
159 def __init__(self, iterable, parent=None, enforce_length=False):
160 self._iterable = iterable
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200161 self._next = iter(iterable).next
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200162 self._length = None
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200163 self.index0 = -1
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200164 self.parent = parent
165 if enforce_length:
166 len(self)
167
168 def make_static(self):
169 """Return a static loop context for the optimizer."""
170 parent = None
171 if self.parent is not None:
172 parent = self.parent.make_static()
173 return StaticLoopContext(self.index0, self.length, parent)
174
175 def __iter__(self):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200176 return self
177
178 def next(self):
179 self.index0 += 1
180 return self._next(), self
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200181
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200182 @property
183 def length(self):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200184 if self._length is None:
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200185 try:
186 length = len(self._iterable)
187 except TypeError:
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200188 self._iterable = tuple(self._iterable)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200189 self._next = iter(self._iterable).next
190 length = len(tuple(self._iterable)) + self.index0 + 1
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200191 self._length = length
192 return self._length
193
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200194 def __repr__(self):
195 return 'LoopContext(%r)' % self.index0
196
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200197
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200198class StaticLoopContext(LoopContextBase):
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200199 """The static loop context is used in the optimizer to "freeze" the
200 status of an iteration. The only reason for this object is if the
201 loop object is accessed in a non static way (eg: becomes part of a
202 function call)."""
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200203
204 def __init__(self, index0, length, parent):
205 self.index0 = index0
206 self.parent = parent
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200207 self.length = length
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200208
209 def __repr__(self):
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200210 """The repr is used by the optimizer to dump the object."""
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200211 return 'StaticLoopContext(%r, %r, %r)' % (
212 self.index0,
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200213 self.length,
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200214 self.parent
215 )
216
217 def make_static(self):
218 return self
219
220
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200221class Macro(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200222 """Wraps a macro."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200223
Armin Ronacherc63243e2008-04-14 22:53:58 +0200224 def __init__(self, environment, func, name, arguments, defaults, catch_all, caller):
225 self._environment = environment
Armin Ronacher71082072008-04-12 14:19:36 +0200226 self._func = func
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200227 self.name = name
228 self.arguments = arguments
229 self.defaults = defaults
230 self.catch_all = catch_all
Armin Ronacher71082072008-04-12 14:19:36 +0200231 self.caller = caller
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200232
233 def __call__(self, *args, **kwargs):
Armin Ronacher9706fab2008-04-08 18:49:56 +0200234 arg_count = len(self.arguments)
235 if len(args) > arg_count:
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200236 raise TypeError('macro %r takes not more than %d argument(s).' %
237 (self.name, len(self.arguments)))
238 arguments = {}
Armin Ronacher9706fab2008-04-08 18:49:56 +0200239 for idx, name in enumerate(self.arguments):
240 try:
241 value = args[idx]
242 except IndexError:
243 try:
244 value = kwargs.pop(name)
245 except KeyError:
246 try:
247 value = self.defaults[idx - arg_count]
248 except IndexError:
Armin Ronacherc63243e2008-04-14 22:53:58 +0200249 value = self._environment.undefined(name,
250 extra='parameter not provided')
Christoph Hackf9f029c2008-04-09 15:08:11 +0200251 arguments['l_' + name] = value
Armin Ronacher71082072008-04-12 14:19:36 +0200252 if self.caller:
253 caller = kwargs.pop('caller', None)
254 if caller is None:
Armin Ronacherc63243e2008-04-14 22:53:58 +0200255 caller = self._environment.undefined('caller',
256 extra='The macro was called from an expression and not '
257 'a call block.')
Armin Ronacher71082072008-04-12 14:19:36 +0200258 arguments['l_caller'] = caller
Armin Ronacher9706fab2008-04-08 18:49:56 +0200259 if self.catch_all:
260 arguments['l_arguments'] = kwargs
Armin Ronacher625215e2008-04-13 16:31:08 +0200261 return self._func(**arguments)
Armin Ronacher71082072008-04-12 14:19:36 +0200262
263 def __repr__(self):
264 return '<%s %s>' % (
265 self.__class__.__name__,
266 self.name is None and 'anonymous' or repr(self.name)
267 )
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200268
269
270class Undefined(object):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200271 """The default undefined implementation. This undefined implementation
272 can be printed and iterated over, but every other access will raise a
273 `NameError`. Custom undefined classes must subclass this.
274 """
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200275
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200276 def __init__(self, name=None, attr=None, extra=None):
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200277 if attr is None:
Armin Ronacher71082072008-04-12 14:19:36 +0200278 self._undefined_hint = '%r is undefined' % name
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200279 self._error_class = NameError
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200280 else:
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200281 self._undefined_hint = '%r has no attribute named %r' \
282 % (name, attr)
283 self._error_class = AttributeError
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200284 if extra is not None:
285 self._undefined_hint += ' (' + extra + ')'
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200286
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200287 def _fail_with_error(self, *args, **kwargs):
288 raise self._error_class(self._undefined_hint)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200289 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
290 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
291 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200292 __getattr__ = __getitem__ = _fail_with_error
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200293
294 def __unicode__(self):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200295 return u''
296
297 def __str__(self):
298 return self.__unicode__().encode('utf-8')
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200299
300 def __repr__(self):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200301 return 'undefined'
Armin Ronacherd1d2f3d2008-04-09 14:02:55 +0200302
303 def __len__(self):
304 return 0
305
306 def __iter__(self):
307 if 0:
308 yield None
Armin Ronacherc63243e2008-04-14 22:53:58 +0200309
310 def __nonzero__(self):
311 return False
312
313
314class DebugUndefined(Undefined):
315 """An undefined that returns the debug info when printed."""
316
317 def __unicode__(self):
318 return u'{{ %s }}' % self._undefined_hint
319
320
321class StrictUndefined(Undefined):
322 """An undefined that barks on print and iteration."""
323
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200324 __iter__ = __unicode__ = __len__ = Undefined._fail_with_error