updated filters: wordwraps uses the wordwrap module and urlize marks the result as...
[jinja2.git] / jinja2 / runtime.py
1 # -*- 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 """
11 import sys
12 from types import FunctionType
13 from itertools import chain, imap
14 from jinja2.utils import Markup, partial, soft_unicode, escape, missing, concat
15 from jinja2.exceptions import UndefinedError, TemplateRuntimeError
16
17
18 # these variables are exported to the template runtime
19 __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
20            'TemplateRuntimeError', 'missing', 'concat', 'escape',
21            'markup_join', 'unicode_join']
22
23
24 def markup_join(*args):
25     """Concatenation that escapes if necessary and converts to unicode."""
26     buf = []
27     iterator = imap(soft_unicode, args)
28     for arg in iterator:
29         buf.append(arg)
30         if hasattr(arg, '__html__'):
31             return Markup(u'').join(chain(buf, iterator))
32     return concat(buf)
33
34
35 def unicode_join(*args):
36     """Simple args to unicode conversion and concatenation."""
37     return concat(imap(unicode, args))
38
39
40 class Context(object):
41     """The template context holds the variables of a template.  It stores the
42     values passed to the template and also the names the template exports.
43     Creating instances is neither supported nor useful as it's created
44     automatically at various stages of the template evaluation and should not
45     be created by hand.
46
47     The context is immutable.  Modifications on :attr:`parent` **must not**
48     happen and modifications on :attr:`vars` are allowed from generated
49     template code only.  Template filters and global functions marked as
50     :func:`contextfunction`\s get the active context passed as first argument
51     and are allowed to access the context read-only.
52
53     The template context supports read only dict operations (`get`,
54     `__getitem__`, `__contains__`) however `__getitem__` doesn't fail with
55     a `KeyError` but returns an :attr:`Undefined` object.
56     """
57
58     def __init__(self, environment, parent, name, blocks):
59         self.parent = parent
60         self.vars = vars = {}
61         self.environment = environment
62         self.exported_vars = set()
63         self.name = name
64
65         # bind functions to the context of environment if required
66         for name, obj in parent.iteritems():
67             if type(obj) is FunctionType:
68                 if getattr(obj, 'contextfunction', 0):
69                     vars[name] = partial(obj, self)
70                 elif getattr(obj, 'environmentfunction', 0):
71                     vars[name] = partial(obj, environment)
72
73         # create the initial mapping of blocks.  Whenever template inheritance
74         # takes place the runtime will update this mapping with the new blocks
75         # from the template.
76         self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
77
78     def super(self, name, current):
79         """Render a parent block."""
80         try:
81             blocks = self.blocks[name]
82             pos = blocks.index(current) - 1
83             if pos < 0:
84                 raise IndexError()
85         except LookupError:
86             return self.environment.undefined('there is no parent block '
87                                               'called %r.' % name,
88                                               name='super')
89         wrap = self.environment.autoescape and Markup or (lambda x: x)
90         render = lambda: wrap(concat(blocks[pos](self)))
91         render.__name__ = render.name = name
92         return render
93
94     def get(self, key, default=None):
95         """Returns an item from the template context, if it doesn't exist
96         `default` is returned.
97         """
98         if key in self.vars:
99             return self.vars[key]
100         if key in self.parent:
101             return self.parent[key]
102         return default
103
104     def get_exported(self):
105         """Get a new dict with the exported variables."""
106         return dict((k, self.vars[k]) for k in self.exported_vars)
107
108     def get_all(self):
109         """Return a copy of the complete context as dict including the
110         global variables.
111         """
112         return dict(self.parent, **self.vars)
113
114     def __contains__(self, name):
115         return name in self.vars or name in self.parent
116
117     def __getitem__(self, key):
118         if key in self.vars:
119             return self.vars[key]
120         if key in self.parent:
121             return self.parent[key]
122         return self.environment.undefined(name=key)
123
124     def __repr__(self):
125         return '<%s %s of %r>' % (
126             self.__class__.__name__,
127             repr(self.get_all()),
128             self.name
129         )
130
131
132 class TemplateReference(object):
133     """The `self` in templates."""
134
135     def __init__(self, context):
136         self.__context = context
137
138     def __getitem__(self, name):
139         func = self.__context.blocks[name][-1]
140         wrap = self.__context.environment.autoescape and \
141                Markup or (lambda x: x)
142         render = lambda: wrap(concat(func(self.__context)))
143         render.__name__ = render.name = name
144         return render
145
146     def __repr__(self):
147         return '<%s %r>' % (
148             self.__class__.__name__,
149             self._context.name
150         )
151
152
153 class LoopContext(object):
154     """A loop context for dynamic iteration."""
155
156     def __init__(self, iterable, enforce_length=False):
157         self._iterable = iterable
158         self._next = iter(iterable).next
159         self._length = None
160         self.index0 = -1
161         if enforce_length:
162             len(self)
163
164     def cycle(self, *args):
165         """A replacement for the old ``{% cycle %}`` tag."""
166         if not args:
167             raise TypeError('no items for cycling given')
168         return args[self.index0 % len(args)]
169
170     first = property(lambda x: x.index0 == 0)
171     last = property(lambda x: x.revindex0 == 0)
172     index = property(lambda x: x.index0 + 1)
173     revindex = property(lambda x: x.length - x.index0)
174     revindex0 = property(lambda x: x.length - x.index)
175
176     def __len__(self):
177         return self.length
178
179     def __iter__(self):
180         return self
181
182     def next(self):
183         self.index0 += 1
184         return self._next(), self
185
186     @property
187     def length(self):
188         if self._length is None:
189             try:
190                 length = len(self._iterable)
191             except TypeError:
192                 self._iterable = tuple(self._iterable)
193                 self._next = iter(self._iterable).next
194                 length = len(tuple(self._iterable)) + self.index0 + 1
195             self._length = length
196         return self._length
197
198     def __repr__(self):
199         return '<%s %r/%r>' % (
200             self.__class__.__name__,
201             self.index,
202             self.length
203         )
204
205
206 class Macro(object):
207     """Wraps a macro."""
208
209     def __init__(self, environment, func, name, arguments, defaults,
210                  catch_kwargs, catch_varargs, caller):
211         self._environment = environment
212         self._func = func
213         self._argument_count = len(arguments)
214         self.name = name
215         self.arguments = arguments
216         self.defaults = defaults
217         self.catch_kwargs = catch_kwargs
218         self.catch_varargs = catch_varargs
219         self.caller = caller
220
221     def __call__(self, *args, **kwargs):
222         arguments = []
223         for idx, name in enumerate(self.arguments):
224             try:
225                 value = args[idx]
226             except:
227                 try:
228                     value = kwargs.pop(name)
229                 except:
230                     try:
231                         value = self.defaults[idx - self._argument_count]
232                     except:
233                         value = self._environment.undefined(
234                             'parameter %r was not provided' % name, name=name)
235             arguments.append(value)
236
237         # it's important that the order of these arguments does not change
238         # if not also changed in the compiler's `function_scoping` method.
239         # the order is caller, keyword arguments, positional arguments!
240         if self.caller:
241             caller = kwargs.pop('caller', None)
242             if caller is None:
243                 caller = self._environment.undefined('No caller defined',
244                                                      name='caller')
245             arguments.append(caller)
246         if self.catch_kwargs:
247             arguments.append(kwargs)
248         elif kwargs:
249             raise TypeError('macro %r takes no keyword argument %r' %
250                             (self.name, iter(kwargs).next()))
251         if self.catch_varargs:
252             arguments.append(args[self._argument_count:])
253         elif len(args) > self._argument_count:
254             raise TypeError('macro %r takes not more than %d argument(s)' %
255                             (self.name, len(self.arguments)))
256         return self._func(*arguments)
257
258     def __repr__(self):
259         return '<%s %s>' % (
260             self.__class__.__name__,
261             self.name is None and 'anonymous' or repr(self.name)
262         )
263
264
265 def fail_with_undefined_error(self, *args, **kwargs):
266     """Regular callback function for undefined objects that raises an
267     `UndefinedError` on call.
268     """
269     if self._undefined_hint is None:
270         if self._undefined_obj is None:
271             hint = '%r is undefined' % self._undefined_name
272         elif not isinstance(self._undefined_name, basestring):
273             hint = '%r object has no element %r' % (
274                 self._undefined_obj.__class__.__name__,
275                 self._undefined_name
276             )
277         else:
278             hint = '%r object has no attribute %r' % (
279                 self._undefined_obj.__class__.__name__,
280                 self._undefined_name
281             )
282     else:
283         hint = self._undefined_hint
284     raise UndefinedError(hint)
285
286
287 class Undefined(object):
288     """The default undefined type.  This undefined type can be printed and
289     iterated over, but every other access will raise an :exc:`UndefinedError`:
290
291     >>> foo = Undefined(name='foo')
292     >>> str(foo)
293     ''
294     >>> not foo
295     True
296     >>> foo + 42
297     Traceback (most recent call last):
298       ...
299     jinja2.exceptions.UndefinedError: 'foo' is undefined
300     """
301     __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name')
302
303     def __init__(self, hint=None, obj=None, name=None):
304         self._undefined_hint = hint
305         self._undefined_obj = obj
306         self._undefined_name = name
307
308     __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
309     __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
310     __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
311     __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
312         fail_with_undefined_error
313
314     def __str__(self):
315         return self.__unicode__().encode('utf-8')
316
317     def __repr__(self):
318         return 'Undefined'
319
320     def __unicode__(self):
321         return u''
322
323     def __len__(self):
324         return 0
325
326     def __iter__(self):
327         if 0:
328             yield None
329
330     def __nonzero__(self):
331         return False
332
333
334 class DebugUndefined(Undefined):
335     """An undefined that returns the debug info when printed.
336
337     >>> foo = DebugUndefined(name='foo')
338     >>> str(foo)
339     '{{ foo }}'
340     >>> not foo
341     True
342     >>> foo + 42
343     Traceback (most recent call last):
344       ...
345     jinja2.exceptions.UndefinedError: 'foo' is undefined
346     """
347     __slots__ = ()
348
349     def __unicode__(self):
350         if self._undefined_hint is None:
351             if self._undefined_obj is None:
352                 return u'{{ %s }}' % self._undefined_name
353             return '{{ no such element: %s[%r] }}' % (
354                 self._undefined_obj.__class__.__name__,
355                 self._undefined_name
356             )
357         return u'{{ undefined value printed: %s }}' % self._undefined_hint
358
359
360 class StrictUndefined(Undefined):
361     """An undefined that barks on print and iteration as well as boolean
362     tests and all kinds of comparisons.  In other words: you can do nothing
363     with it except checking if it's defined using the `defined` test.
364
365     >>> foo = StrictUndefined(name='foo')
366     >>> str(foo)
367     Traceback (most recent call last):
368       ...
369     jinja2.exceptions.UndefinedError: 'foo' is undefined
370     >>> not foo
371     Traceback (most recent call last):
372       ...
373     jinja2.exceptions.UndefinedError: 'foo' is undefined
374     >>> foo + 42
375     Traceback (most recent call last):
376       ...
377     jinja2.exceptions.UndefinedError: 'foo' is undefined
378     """
379     __slots__ = ()
380     __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
381         fail_with_undefined_error
382
383
384 # remove remaining slots attributes, after the metaclass did the magic they
385 # are unneeded and irritating as they contain wrong data for the subclasses.
386 del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__