1 # -*- coding: utf-8 -*-
8 :copyright: Copyright 2008 by Armin Ronacher.
12 from types import FunctionType, MethodType
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
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']
23 _context_function_types = (FunctionType, MethodType)
27 """Concatenation that escapes if necessary and converts to unicode."""
29 iterator = imap(soft_unicode, seq)
32 if hasattr(arg, '__html__'):
33 return Markup(u'').join(chain(buf, iterator))
37 def unicode_join(seq):
38 """Simple args to unicode conversion and concatenation."""
39 return concat(imap(unicode, seq))
42 class Context(object):
43 """The template context holds the variables of a template. It stores the
44 values passed to the template and also the names the template exports.
45 Creating instances is neither supported nor useful as it's created
46 automatically at various stages of the template evaluation and should not
49 The context is immutable. Modifications on :attr:`parent` **must not**
50 happen and modifications on :attr:`vars` are allowed from generated
51 template code only. Template filters and global functions marked as
52 :func:`contextfunction`\s get the active context passed as first argument
53 and are allowed to access the context read-only.
55 The template context supports read only dict operations (`get`,
56 `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
57 `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
58 method that doesn't fail with a `KeyError` but returns an
59 :class:`Undefined` object for missing variables.
61 __slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
64 def __init__(self, environment, parent, name, blocks):
67 self.environment = environment
68 self.exported_vars = set()
71 # create the initial mapping of blocks. Whenever template inheritance
72 # takes place the runtime will update this mapping with the new blocks
74 self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
76 def super(self, name, current):
77 """Render a parent block."""
79 blocks = self.blocks[name]
80 block = blocks[blocks.index(current) + 1]
82 return self.environment.undefined('there is no parent block '
85 wrap = self.environment.autoescape and Markup or (lambda x: x)
86 render = lambda: wrap(concat(block(self)))
87 render.__name__ = render.name = name
90 def get(self, key, default=None):
91 """Returns an item from the template context, if it doesn't exist
92 `default` is returned.
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.
104 return self.vars[key]
105 if key in self.parent:
106 return self.parent[key]
107 return self.environment.undefined(name=key)
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)
114 """Return a copy of the complete context as dict including the
117 return dict(self.parent, **self.vars)
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`.
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)
135 proxy = lambda self: getattr(self.get_all(), meth)()
136 proxy.__doc__ = getattr(dict, meth).__doc__
137 proxy.__name__ = meth
141 values = _all('values')
142 items = _all('items')
143 iterkeys = _all('iterkeys')
144 itervalues = _all('itervalues')
145 iteritems = _all('iteritems')
148 def __contains__(self, name):
149 return name in self.vars or name in self.parent
151 def __getitem__(self, key):
152 """Lookup a variable or raise `KeyError` if the variable is
155 item = self.resolve(key)
156 if isinstance(item, Undefined):
161 return '<%s %s of %r>' % (
162 self.__class__.__name__,
163 repr(self.get_all()),
168 # register the context as mutable mapping if possible
170 from collections import MutableMapping
171 MutableMapping.register(Context)
176 class TemplateReference(object):
177 """The `self` in templates."""
179 def __init__(self, context):
180 self.__context = context
182 def __getitem__(self, name):
183 func = self.__context.blocks[name][0]
184 wrap = self.__context.environment.autoescape and \
185 Markup or (lambda x: x)
186 render = lambda: wrap(concat(func(self.__context)))
187 render.__name__ = render.name = name
192 self.__class__.__name__,
197 class LoopContext(object):
198 """A loop context for dynamic iteration."""
200 def __init__(self, iterable, enforce_length=False, recurse=None):
201 self._iterable = iterable
202 self._next = iter(iterable).next
204 self._recurse = recurse
209 def cycle(self, *args):
210 """Cycles among the arguments with the current loop index."""
212 raise TypeError('no items for cycling given')
213 return args[self.index0 % len(args)]
215 first = property(lambda x: x.index0 == 0)
216 last = property(lambda x: x.revindex0 == 0)
217 index = property(lambda x: x.index0 + 1)
218 revindex = property(lambda x: x.length - x.index0)
219 revindex0 = property(lambda x: x.length - x.index)
225 return LoopContextIterator(self)
227 def loop(self, iterable):
228 if self._recurse is None:
229 raise TypeError('Tried to call non recursive loop. Maybe you '
230 "forgot the 'recursive' modifier.")
231 return self._recurse(iterable, self._recurse)
233 # a nifty trick to enhance the error message if someone tried to call
234 # the the loop without or with too many arguments.
235 __call__ = loop; del loop
239 if self._length is None:
241 # first try to get the length from the iterable (if the
242 # iterable is a sequence)
243 length = len(self._iterable)
245 # if that's not possible (ie: iterating over a generator)
246 # we have to convert the iterable into a sequence and
247 # use the length of that.
248 self._iterable = tuple(self._iterable)
249 self._next = iter(self._iterable).next
250 length = len(tuple(self._iterable)) + self.index0 + 1
251 self._length = length
255 return '<%s %r/%r>' % (
256 self.__class__.__name__,
262 class LoopContextIterator(object):
263 """The iterator for a loop context."""
264 __slots__ = ('context',)
266 def __init__(self, context):
267 self.context = context
275 return ctx._next(), ctx
281 def __init__(self, environment, func, name, arguments, defaults,
282 catch_kwargs, catch_varargs, caller):
283 self._environment = environment
285 self._argument_count = len(arguments)
287 self.arguments = arguments
288 self.defaults = defaults
289 self.catch_kwargs = catch_kwargs
290 self.catch_varargs = catch_varargs
293 def __call__(self, *args, **kwargs):
295 for idx, name in enumerate(self.arguments):
300 value = kwargs.pop(name)
303 value = self.defaults[idx - self._argument_count]
305 value = self._environment.undefined(
306 'parameter %r was not provided' % name, name=name)
307 arguments.append(value)
309 # it's important that the order of these arguments does not change
310 # if not also changed in the compiler's `function_scoping` method.
311 # the order is caller, keyword arguments, positional arguments!
313 caller = kwargs.pop('caller', None)
315 caller = self._environment.undefined('No caller defined',
317 arguments.append(caller)
318 if self.catch_kwargs:
319 arguments.append(kwargs)
321 raise TypeError('macro %r takes no keyword argument %r' %
322 (self.name, iter(kwargs).next()))
323 if self.catch_varargs:
324 arguments.append(args[self._argument_count:])
325 elif len(args) > self._argument_count:
326 raise TypeError('macro %r takes not more than %d argument(s)' %
327 (self.name, len(self.arguments)))
328 return self._func(*arguments)
332 self.__class__.__name__,
333 self.name is None and 'anonymous' or repr(self.name)
337 class Undefined(object):
338 """The default undefined type. This undefined type can be printed and
339 iterated over, but every other access will raise an :exc:`UndefinedError`:
341 >>> foo = Undefined(name='foo')
347 Traceback (most recent call last):
349 jinja2.exceptions.UndefinedError: 'foo' is undefined
351 __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
352 '_undefined_exception')
354 def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
355 self._undefined_hint = hint
356 self._undefined_obj = obj
357 self._undefined_name = name
358 self._undefined_exception = exc
360 def _fail_with_undefined_error(self, *args, **kwargs):
361 """Regular callback function for undefined objects that raises an
362 `UndefinedError` on call.
364 if self._undefined_hint is None:
365 if self._undefined_obj is None:
366 hint = '%r is undefined' % self._undefined_name
367 elif not isinstance(self._undefined_name, basestring):
368 hint = '%r object has no element %r' % (
369 self._undefined_obj.__class__.__name__,
373 hint = '%r object has no attribute %r' % (
374 self._undefined_obj.__class__.__name__,
378 hint = self._undefined_hint
379 raise self._undefined_exception(hint)
381 __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
382 __realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
383 __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
384 __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
385 __int__ = __float__ = __complex__ = _fail_with_undefined_error
388 return self.__unicode__().encode('utf-8')
390 def __unicode__(self):
400 def __nonzero__(self):
407 class DebugUndefined(Undefined):
408 """An undefined that returns the debug info when printed.
410 >>> foo = DebugUndefined(name='foo')
416 Traceback (most recent call last):
418 jinja2.exceptions.UndefinedError: 'foo' is undefined
422 def __unicode__(self):
423 if self._undefined_hint is None:
424 if self._undefined_obj is None:
425 return u'{{ %s }}' % self._undefined_name
426 return '{{ no such element: %s[%r] }}' % (
427 self._undefined_obj.__class__.__name__,
430 return u'{{ undefined value printed: %s }}' % self._undefined_hint
433 class StrictUndefined(Undefined):
434 """An undefined that barks on print and iteration as well as boolean
435 tests and all kinds of comparisons. In other words: you can do nothing
436 with it except checking if it's defined using the `defined` test.
438 >>> foo = StrictUndefined(name='foo')
440 Traceback (most recent call last):
442 jinja2.exceptions.UndefinedError: 'foo' is undefined
444 Traceback (most recent call last):
446 jinja2.exceptions.UndefinedError: 'foo' is undefined
448 Traceback (most recent call last):
450 jinja2.exceptions.UndefinedError: 'foo' is undefined
453 __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
454 Undefined._fail_with_undefined_error
457 # remove remaining slots attributes, after the metaclass did the magic they
458 # are unneeded and irritating as they contain wrong data for the subclasses.
459 del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__