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