1 # -*- coding: utf-8 -*-
8 :copyright: Copyright 2008 by Armin Ronacher.
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, \
19 # these variables are exported to the template runtime
20 __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
21 'TemplateRuntimeError', 'missing', 'concat', 'escape',
22 'markup_join', 'unicode_join', 'TemplateNotFound']
25 #: the types we support for context functions
26 _context_function_types = (FunctionType, MethodType)
30 """Concatenation that escapes if necessary and converts to unicode."""
32 iterator = imap(soft_unicode, seq)
35 if hasattr(arg, '__html__'):
36 return Markup(u'').join(chain(buf, iterator))
40 def unicode_join(seq):
41 """Simple args to unicode conversion and concatenation."""
42 return concat(imap(unicode, seq))
45 class Context(object):
46 """The template context holds the variables of a template. It stores the
47 values passed to the template and also the names the template exports.
48 Creating instances is neither supported nor useful as it's created
49 automatically at various stages of the template evaluation and should not
52 The context is immutable. Modifications on :attr:`parent` **must not**
53 happen and modifications on :attr:`vars` are allowed from generated
54 template code only. Template filters and global functions marked as
55 :func:`contextfunction`\s get the active context passed as first argument
56 and are allowed to access the context read-only.
58 The template context supports read only dict operations (`get`,
59 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
60 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
61 method that doesn't fail with a `KeyError` but returns an
62 :class:`Undefined` object for missing variables.
64 __slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
65 'blocks', '__weakref__')
67 def __init__(self, environment, parent, name, blocks):
70 self.environment = environment
71 self.exported_vars = set()
74 # create the initial mapping of blocks. Whenever template inheritance
75 # takes place the runtime will update this mapping with the new blocks
77 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
79 def super(self, name, current):
80 """Render a parent block."""
82 blocks = self.blocks[name]
83 index = blocks.index(current) + 1
86 return self.environment.undefined('there is no parent block '
89 return BlockReference(name, self, blocks, index)
91 def get(self, key, default=None):
92 """Returns an item from the template context, if it doesn't exist
93 `default` is returned.
100 def resolve(self, key):
101 """Looks up a variable like `__getitem__` or `get` but returns an
102 :class:`Undefined` object with the name of the name looked up.
105 return self.vars[key]
106 if key in self.parent:
107 return self.parent[key]
108 return self.environment.undefined(name=key)
110 def get_exported(self):
111 """Get a new dict with the exported variables."""
112 return dict((k, self.vars[k]) for k in self.exported_vars)
115 """Return a copy of the complete context as dict including the
118 return dict(self.parent, **self.vars)
120 def call(__self, __obj, *args, **kwargs):
121 """Call the callable with the arguments and keyword arguments
122 provided but inject the active context or environment as first
123 argument if the callable is a :func:`contextfunction` or
124 :func:`environmentfunction`.
127 __traceback_hide__ = True
128 if isinstance(__obj, _context_function_types):
129 if getattr(__obj, 'contextfunction', 0):
130 args = (__self,) + args
131 elif getattr(__obj, 'environmentfunction', 0):
132 args = (__self.environment,) + args
133 return __obj(*args, **kwargs)
136 proxy = lambda self: getattr(self.get_all(), meth)()
137 proxy.__doc__ = getattr(dict, meth).__doc__
138 proxy.__name__ = meth
142 values = _all('values')
143 items = _all('items')
144 iterkeys = _all('iterkeys')
145 itervalues = _all('itervalues')
146 iteritems = _all('iteritems')
149 def __contains__(self, name):
150 return name in self.vars or name in self.parent
152 def __getitem__(self, key):
153 """Lookup a variable or raise `KeyError` if the variable is
156 item = self.resolve(key)
157 if isinstance(item, Undefined):
162 return '<%s %s of %r>' % (
163 self.__class__.__name__,
164 repr(self.get_all()),
169 # register the context as mapping if possible
171 from collections import Mapping
172 Mapping.register(Context)
177 class TemplateReference(object):
178 """The `self` in templates."""
180 def __init__(self, context):
181 self.__context = context
183 def __getitem__(self, name):
184 blocks = self.__context.blocks[name]
185 wrap = self.__context.environment.autoescape and \
186 Markup or (lambda x: x)
187 return BlockReference(name, self.__context, blocks, 0)
191 self.__class__.__name__,
196 class BlockReference(object):
197 """One block on a template reference."""
199 def __init__(self, name, context, stack, depth):
201 self._context = context
207 """Super the block."""
208 if self._depth + 1 >= len(self._stack):
209 return self._context.environment. \
210 undefined('there is no parent block called %r.' %
211 self.name, name='super')
212 return BlockReference(self.name, self._context, self._stack,
216 rv = concat(self._stack[self._depth](self._context))
217 if self._context.environment.autoescape:
222 class LoopContext(object):
223 """A loop context for dynamic iteration."""
225 def __init__(self, iterable, recurse=None):
226 self._iterator = iter(iterable)
227 self._recurse = recurse
230 # try to get the length of the iterable early. This must be done
231 # here because there are some broken iterators around where there
232 # __len__ is the number of iterations left (i'm looking at your
233 # listreverseiterator!).
235 self._length = len(iterable)
236 except (TypeError, AttributeError):
239 def cycle(self, *args):
240 """Cycles among the arguments with the current loop index."""
242 raise TypeError('no items for cycling given')
243 return args[self.index0 % len(args)]
245 first = property(lambda x: x.index0 == 0)
246 last = property(lambda x: x.index0 + 1 == x.length)
247 index = property(lambda x: x.index0 + 1)
248 revindex = property(lambda x: x.length - x.index0)
249 revindex0 = property(lambda x: x.length - x.index)
255 return LoopContextIterator(self)
257 def loop(self, iterable):
258 if self._recurse is None:
259 raise TypeError('Tried to call non recursive loop. Maybe you '
260 "forgot the 'recursive' modifier.")
261 return self._recurse(iterable, self._recurse)
263 # a nifty trick to enhance the error message if someone tried to call
264 # the the loop without or with too many arguments.
265 __call__ = loop; del loop
269 if self._length is None:
270 # if was not possible to get the length of the iterator when
271 # the loop context was created (ie: iterating over a generator)
272 # we have to convert the iterable into a sequence and use the
274 iterable = tuple(self._iterator)
275 self._iterator = iter(iterable)
276 self._length = len(iterable) + self.index0 + 1
280 return '<%s %r/%r>' % (
281 self.__class__.__name__,
287 class LoopContextIterator(object):
288 """The iterator for a loop context."""
289 __slots__ = ('context',)
291 def __init__(self, context):
292 self.context = context
300 return ctx._iterator.next(), ctx
306 def __init__(self, environment, func, name, arguments, defaults,
307 catch_kwargs, catch_varargs, caller):
308 self._environment = environment
310 self._argument_count = len(arguments)
312 self.arguments = arguments
313 self.defaults = defaults
314 self.catch_kwargs = catch_kwargs
315 self.catch_varargs = catch_varargs
318 def __call__(self, *args, **kwargs):
320 for idx, name in enumerate(self.arguments):
325 value = kwargs.pop(name)
328 value = self.defaults[idx - self._argument_count]
330 value = self._environment.undefined(
331 'parameter %r was not provided' % name, name=name)
332 arguments.append(value)
334 # it's important that the order of these arguments does not change
335 # if not also changed in the compiler's `function_scoping` method.
336 # the order is caller, keyword arguments, positional arguments!
338 caller = kwargs.pop('caller', None)
340 caller = self._environment.undefined('No caller defined',
342 arguments.append(caller)
343 if self.catch_kwargs:
344 arguments.append(kwargs)
346 raise TypeError('macro %r takes no keyword argument %r' %
347 (self.name, iter(kwargs).next()))
348 if self.catch_varargs:
349 arguments.append(args[self._argument_count:])
350 elif len(args) > self._argument_count:
351 raise TypeError('macro %r takes not more than %d argument(s)' %
352 (self.name, len(self.arguments)))
353 return self._func(*arguments)
357 self.__class__.__name__,
358 self.name is None and 'anonymous' or repr(self.name)
362 class Undefined(object):
363 """The default undefined type. This undefined type can be printed and
364 iterated over, but every other access will raise an :exc:`UndefinedError`:
366 >>> foo = Undefined(name='foo')
372 Traceback (most recent call last):
374 UndefinedError: 'foo' is undefined
376 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
377 '_undefined_exception')
379 def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
380 self._undefined_hint = hint
381 self._undefined_obj = obj
382 self._undefined_name = name
383 self._undefined_exception = exc
385 def _fail_with_undefined_error(self, *args, **kwargs):
386 """Regular callback function for undefined objects that raises an
387 `UndefinedError` on call.
389 if self._undefined_hint is None:
390 if self._undefined_obj is None:
391 hint = '%r is undefined' % self._undefined_name
392 elif not isinstance(self._undefined_name, basestring):
393 hint = '%r object has no element %r' % (
394 self._undefined_obj.__class__.__name__,
398 hint = '%r object has no attribute %r' % (
399 self._undefined_obj.__class__.__name__,
403 hint = self._undefined_hint
404 raise self._undefined_exception(hint)
406 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
407 __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
408 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
409 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
410 __int__ = __float__ = __complex__ = __pow__ = __rpow__ = \
411 _fail_with_undefined_error
414 return unicode(self).encode('utf-8')
416 def __unicode__(self):
426 def __nonzero__(self):
433 class DebugUndefined(Undefined):
434 """An undefined that returns the debug info when printed.
436 >>> foo = DebugUndefined(name='foo')
442 Traceback (most recent call last):
444 UndefinedError: 'foo' is undefined
448 def __unicode__(self):
449 if self._undefined_hint is None:
450 if self._undefined_obj is None:
451 return u'{{ %s }}' % self._undefined_name
452 return '{{ no such element: %s[%r] }}' % (
453 self._undefined_obj.__class__.__name__,
456 return u'{{ undefined value printed: %s }}' % self._undefined_hint
459 class StrictUndefined(Undefined):
460 """An undefined that barks on print and iteration as well as boolean
461 tests and all kinds of comparisons. In other words: you can do nothing
462 with it except checking if it's defined using the `defined` test.
464 >>> foo = StrictUndefined(name='foo')
466 Traceback (most recent call last):
468 UndefinedError: 'foo' is undefined
470 Traceback (most recent call last):
472 UndefinedError: 'foo' is undefined
474 Traceback (most recent call last):
476 UndefinedError: 'foo' is undefined
479 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
480 Undefined._fail_with_undefined_error
483 # remove remaining slots attributes, after the metaclass did the magic they
484 # are unneeded and irritating as they contain wrong data for the subclasses.
485 del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__