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