Added first version of IR importer/exporter
[libfirm] / scripts / 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: BSD.
10 """
11 import sys
12 from itertools import chain, imap
13 from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
14      concat, MethodType, FunctionType
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 #: the types we support for context functions
25 _context_function_types = (FunctionType, MethodType)
26
27
28 def markup_join(seq):
29     """Concatenation that escapes if necessary and converts to unicode."""
30     buf = []
31     iterator = imap(soft_unicode, seq)
32     for arg in iterator:
33         buf.append(arg)
34         if hasattr(arg, '__html__'):
35             return Markup(u'').join(chain(buf, iterator))
36     return concat(buf)
37
38
39 def unicode_join(seq):
40     """Simple args to unicode conversion and concatenation."""
41     return concat(imap(unicode, seq))
42
43
44 class Context(object):
45     """The template context holds the variables of a template.  It stores the
46     values passed to the template and also the names the template exports.
47     Creating instances is neither supported nor useful as it's created
48     automatically at various stages of the template evaluation and should not
49     be created by hand.
50
51     The context is immutable.  Modifications on :attr:`parent` **must not**
52     happen and modifications on :attr:`vars` are allowed from generated
53     template code only.  Template filters and global functions marked as
54     :func:`contextfunction`\s get the active context passed as first argument
55     and are allowed to access the context read-only.
56
57     The template context supports read only dict operations (`get`,
58     `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
59     `__getitem__`, `__contains__`).  Additionally there is a :meth:`resolve`
60     method that doesn't fail with a `KeyError` but returns an
61     :class:`Undefined` object for missing variables.
62     """
63     __slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
64                  'blocks', '__weakref__')
65
66     def __init__(self, environment, parent, name, blocks):
67         self.parent = parent
68         self.vars = vars = {}
69         self.environment = environment
70         self.exported_vars = set()
71         self.name = name
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             index = blocks.index(current) + 1
83             blocks[index]
84         except LookupError:
85             return self.environment.undefined('there is no parent block '
86                                               'called %r.' % name,
87                                               name='super')
88         return BlockReference(name, self, blocks, index)
89
90     def get(self, key, default=None):
91         """Returns an item from the template context, if it doesn't exist
92         `default` is returned.
93         """
94         try:
95             return self[key]
96         except KeyError:
97             return default
98
99     def resolve(self, key):
100         """Looks up a variable like `__getitem__` or `get` but returns an
101         :class:`Undefined` object with the name of the name looked up.
102         """
103         if key in self.vars:
104             return self.vars[key]
105         if key in self.parent:
106             return self.parent[key]
107         return self.environment.undefined(name=key)
108
109     def get_exported(self):
110         """Get a new dict with the exported variables."""
111         return dict((k, self.vars[k]) for k in self.exported_vars)
112
113     def get_all(self):
114         """Return a copy of the complete context as dict including the
115         exported variables.
116         """
117         return dict(self.parent, **self.vars)
118
119     def call(__self, __obj, *args, **kwargs):
120         """Call the callable with the arguments and keyword arguments
121         provided but inject the active context or environment as first
122         argument if the callable is a :func:`contextfunction` or
123         :func:`environmentfunction`.
124         """
125         if __debug__:
126             __traceback_hide__ = True
127         if isinstance(__obj, _context_function_types):
128             if getattr(__obj, 'contextfunction', 0):
129                 args = (__self,) + args
130             elif getattr(__obj, 'environmentfunction', 0):
131                 args = (__self.environment,) + args
132         return __obj(*args, **kwargs)
133
134     def _all(meth):
135         proxy = lambda self: getattr(self.get_all(), meth)()
136         proxy.__doc__ = getattr(dict, meth).__doc__
137         proxy.__name__ = meth
138         return proxy
139
140     keys = _all('keys')
141     values = _all('values')
142     items = _all('items')
143     iterkeys = _all('iterkeys')
144     itervalues = _all('itervalues')
145     iteritems = _all('iteritems')
146     del _all
147
148     def __contains__(self, name):
149         return name in self.vars or name in self.parent
150
151     def __getitem__(self, key):
152         """Lookup a variable or raise `KeyError` if the variable is
153         undefined.
154         """
155         item = self.resolve(key)
156         if isinstance(item, Undefined):
157             raise KeyError(key)
158         return item
159
160     def __repr__(self):
161         return '<%s %s of %r>' % (
162             self.__class__.__name__,
163             repr(self.get_all()),
164             self.name
165         )
166
167
168 # register the context as mapping if possible
169 try:
170     from collections import Mapping
171     Mapping.register(Context)
172 except ImportError:
173     pass
174
175
176 class TemplateReference(object):
177     """The `self` in templates."""
178
179     def __init__(self, context):
180         self.__context = context
181
182     def __getitem__(self, name):
183         blocks = self.__context.blocks[name]
184         wrap = self.__context.environment.autoescape and \
185                Markup or (lambda x: x)
186         return BlockReference(name, self.__context, blocks, 0)
187
188     def __repr__(self):
189         return '<%s %r>' % (
190             self.__class__.__name__,
191             self.__context.name
192         )
193
194
195 class BlockReference(object):
196     """One block on a template reference."""
197
198     def __init__(self, name, context, stack, depth):
199         self.name = name
200         self._context = context
201         self._stack = stack
202         self._depth = depth
203
204     @property
205     def super(self):
206         """Super the block."""
207         if self._depth + 1 >= len(self._stack):
208             return self._context.environment. \
209                 undefined('there is no parent block called %r.' %
210                           self.name, name='super')
211         return BlockReference(self.name, self._context, self._stack,
212                               self._depth + 1)
213
214     def __call__(self):
215         rv = concat(self._stack[self._depth](self._context))
216         if self._context.environment.autoescape:
217             rv = Markup(rv)
218         return rv
219
220
221 class LoopContext(object):
222     """A loop context for dynamic iteration."""
223
224     def __init__(self, iterable, recurse=None):
225         self._iterator = iter(iterable)
226         self._recurse = recurse
227         self.index0 = -1
228
229         # try to get the length of the iterable early.  This must be done
230         # here because there are some broken iterators around where there
231         # __len__ is the number of iterations left (i'm looking at your
232         # listreverseiterator!).
233         try:
234             self._length = len(iterable)
235         except (TypeError, AttributeError):
236             self._length = None
237
238     def cycle(self, *args):
239         """Cycles among the arguments with the current loop index."""
240         if not args:
241             raise TypeError('no items for cycling given')
242         return args[self.index0 % len(args)]
243
244     first = property(lambda x: x.index0 == 0)
245     last = property(lambda x: x.index0 + 1 == x.length)
246     index = property(lambda x: x.index0 + 1)
247     revindex = property(lambda x: x.length - x.index0)
248     revindex0 = property(lambda x: x.length - x.index)
249
250     def __len__(self):
251         return self.length
252
253     def __iter__(self):
254         return LoopContextIterator(self)
255
256     def loop(self, iterable):
257         if self._recurse is None:
258             raise TypeError('Tried to call non recursive loop.  Maybe you '
259                             "forgot the 'recursive' modifier.")
260         return self._recurse(iterable, self._recurse)
261
262     # a nifty trick to enhance the error message if someone tried to call
263     # the the loop without or with too many arguments.
264     __call__ = loop; del loop
265
266     @property
267     def length(self):
268         if self._length is None:
269             # if was not possible to get the length of the iterator when
270             # the loop context was created (ie: iterating over a generator)
271             # we have to convert the iterable into a sequence and use the
272             # length of that.
273             iterable = tuple(self._iterator)
274             self._iterator = iter(iterable)
275             self._length = len(iterable) + self.index0 + 1
276         return self._length
277
278     def __repr__(self):
279         return '<%s %r/%r>' % (
280             self.__class__.__name__,
281             self.index,
282             self.length
283         )
284
285
286 class LoopContextIterator(object):
287     """The iterator for a loop context."""
288     __slots__ = ('context',)
289
290     def __init__(self, context):
291         self.context = context
292
293     def __iter__(self):
294         return self
295
296     def next(self):
297         ctx = self.context
298         ctx.index0 += 1
299         return ctx._iterator.next(), ctx
300
301
302 class Macro(object):
303     """Wraps a macro."""
304
305     def __init__(self, environment, func, name, arguments, defaults,
306                  catch_kwargs, catch_varargs, caller):
307         self._environment = environment
308         self._func = func
309         self._argument_count = len(arguments)
310         self.name = name
311         self.arguments = arguments
312         self.defaults = defaults
313         self.catch_kwargs = catch_kwargs
314         self.catch_varargs = catch_varargs
315         self.caller = caller
316
317     def __call__(self, *args, **kwargs):
318         arguments = []
319         for idx, name in enumerate(self.arguments):
320             try:
321                 value = args[idx]
322             except:
323                 try:
324                     value = kwargs.pop(name)
325                 except:
326                     try:
327                         value = self.defaults[idx - self._argument_count]
328                     except:
329                         value = self._environment.undefined(
330                             'parameter %r was not provided' % name, name=name)
331             arguments.append(value)
332
333         # it's important that the order of these arguments does not change
334         # if not also changed in the compiler's `function_scoping` method.
335         # the order is caller, keyword arguments, positional arguments!
336         if self.caller:
337             caller = kwargs.pop('caller', None)
338             if caller is None:
339                 caller = self._environment.undefined('No caller defined',
340                                                      name='caller')
341             arguments.append(caller)
342         if self.catch_kwargs:
343             arguments.append(kwargs)
344         elif kwargs:
345             raise TypeError('macro %r takes no keyword argument %r' %
346                             (self.name, iter(kwargs).next()))
347         if self.catch_varargs:
348             arguments.append(args[self._argument_count:])
349         elif len(args) > self._argument_count:
350             raise TypeError('macro %r takes not more than %d argument(s)' %
351                             (self.name, len(self.arguments)))
352         return self._func(*arguments)
353
354     def __repr__(self):
355         return '<%s %s>' % (
356             self.__class__.__name__,
357             self.name is None and 'anonymous' or repr(self.name)
358         )
359
360
361 class Undefined(object):
362     """The default undefined type.  This undefined type can be printed and
363     iterated over, but every other access will raise an :exc:`UndefinedError`:
364
365     >>> foo = Undefined(name='foo')
366     >>> str(foo)
367     ''
368     >>> not foo
369     True
370     >>> foo + 42
371     Traceback (most recent call last):
372       ...
373     UndefinedError: 'foo' is undefined
374     """
375     __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
376                  '_undefined_exception')
377
378     def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
379         self._undefined_hint = hint
380         self._undefined_obj = obj
381         self._undefined_name = name
382         self._undefined_exception = exc
383
384     def _fail_with_undefined_error(self, *args, **kwargs):
385         """Regular callback function for undefined objects that raises an
386         `UndefinedError` on call.
387         """
388         if self._undefined_hint is None:
389             if self._undefined_obj is None:
390                 hint = '%r is undefined' % self._undefined_name
391             elif not isinstance(self._undefined_name, basestring):
392                 hint = '%r object has no element %r' % (
393                     self._undefined_obj.__class__.__name__,
394                     self._undefined_name
395                 )
396             else:
397                 hint = '%r object has no attribute %r' % (
398                     self._undefined_obj.__class__.__name__,
399                     self._undefined_name
400                 )
401         else:
402             hint = self._undefined_hint
403         raise self._undefined_exception(hint)
404
405     __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
406     __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
407     __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
408     __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
409     __int__ = __float__ = __complex__ = __pow__ = __rpow__ = \
410         _fail_with_undefined_error
411
412     def __str__(self):
413         return unicode(self).encode('utf-8')
414
415     def __unicode__(self):
416         return u''
417
418     def __len__(self):
419         return 0
420
421     def __iter__(self):
422         if 0:
423             yield None
424
425     def __nonzero__(self):
426         return False
427
428     def __repr__(self):
429         return 'Undefined'
430
431
432 class DebugUndefined(Undefined):
433     """An undefined that returns the debug info when printed.
434
435     >>> foo = DebugUndefined(name='foo')
436     >>> str(foo)
437     '{{ foo }}'
438     >>> not foo
439     True
440     >>> foo + 42
441     Traceback (most recent call last):
442       ...
443     UndefinedError: 'foo' is undefined
444     """
445     __slots__ = ()
446
447     def __unicode__(self):
448         if self._undefined_hint is None:
449             if self._undefined_obj is None:
450                 return u'{{ %s }}' % self._undefined_name
451             return '{{ no such element: %s[%r] }}' % (
452                 self._undefined_obj.__class__.__name__,
453                 self._undefined_name
454             )
455         return u'{{ undefined value printed: %s }}' % self._undefined_hint
456
457
458 class StrictUndefined(Undefined):
459     """An undefined that barks on print and iteration as well as boolean
460     tests and all kinds of comparisons.  In other words: you can do nothing
461     with it except checking if it's defined using the `defined` test.
462
463     >>> foo = StrictUndefined(name='foo')
464     >>> str(foo)
465     Traceback (most recent call last):
466       ...
467     UndefinedError: 'foo' is undefined
468     >>> not foo
469     Traceback (most recent call last):
470       ...
471     UndefinedError: 'foo' is undefined
472     >>> foo + 42
473     Traceback (most recent call last):
474       ...
475     UndefinedError: 'foo' is undefined
476     """
477     __slots__ = ()
478     __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
479         Undefined._fail_with_undefined_error
480
481
482 # remove remaining slots attributes, after the metaclass did the magic they
483 # are unneeded and irritating as they contain wrong data for the subclasses.
484 del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__