1 # -*- coding: utf-8 -*-
8 :copyright: (c) 2010 by the Jinja Team.
11 from itertools
import chain
12 from jinja2
.nodes
import EvalContext
, _context_function_types
13 from jinja2
.utils
import Markup
, soft_unicode
, escape
, missing
, concat
, \
14 internalcode
, object_type_repr
15 from jinja2
.exceptions
import UndefinedError
, TemplateRuntimeError
, \
17 from jinja2
._compat
import next
, imap
, text_type
, iteritems
, \
18 implements_iterator
, implements_to_string
, string_types
, PY2
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',
27 #: the name of the function that is used to convert something into
28 #: a string. We can just use the text type here.
31 #: the identity function. Useful for certain things in the environment
32 identity
= lambda x
: x
34 _last_iteration
= object()
38 """Concatenation that escapes if necessary and converts to unicode."""
40 iterator
= imap(soft_unicode
, seq
)
43 if hasattr(arg
, '__html__'):
44 return Markup(u
'').join(chain(buf
, iterator
))
48 def unicode_join(seq
):
49 """Simple args to unicode conversion and concatenation."""
50 return concat(imap(text_type
, seq
))
53 def new_context(environment
, template_name
, blocks
, vars=None,
54 shared
=None, globals=None, locals=None):
55 """Internal helper to for context creation."""
61 parent
= dict(globals or (), **vars)
63 # if the parent is shared a copy should be created because
64 # we don't want to modify the dict passed
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
)
73 class TemplateReference(object):
74 """The `self` in templates."""
76 def __init__(self
, context
):
77 self
.__context
= context
79 def __getitem__(self
, name
):
80 blocks
= self
.__context
.blocks
[name
]
81 return BlockReference(name
, self
.__context
, blocks
, 0)
85 self
.__class
__.__name
__,
90 class 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
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.
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.
109 __slots__
= ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
110 'name', 'blocks', '__weakref__')
112 def __init__(self
, environment
, parent
, name
, blocks
):
115 self
.environment
= environment
116 self
.eval_ctx
= EvalContext(self
.environment
, name
)
117 self
.exported_vars
= set()
120 # create the initial mapping of blocks. Whenever template inheritance
121 # takes place the runtime will update this mapping with the new blocks
123 self
.blocks
= dict((k
, [v
]) for k
, v
in iteritems(blocks
))
125 def super(self
, name
, current
):
126 """Render a parent block."""
128 blocks
= self
.blocks
[name
]
129 index
= blocks
.index(current
) + 1
132 return self
.environment
.undefined('there is no parent block '
135 return BlockReference(name
, self
, blocks
, index
)
137 def get(self
, key
, default
=None):
138 """Returns an item from the template context, if it doesn't exist
139 `default` is returned.
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.
151 return self
.vars[key
]
152 if key
in self
.parent
:
153 return self
.parent
[key
]
154 return self
.environment
.undefined(name
=key
)
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
)
161 """Return a copy of the complete context as dict including the
164 return dict(self
.parent
, **self
.vars)
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`.
174 __traceback_hide__
= True
176 # Allow callable classes to take a context
178 for fn_type
in ('contextfunction',
179 'evalcontextfunction',
180 'environmentfunction'):
181 if hasattr(fn
, fn_type
):
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
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')
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
))
209 proxy
= lambda self
: getattr(self
.get_all(), meth
)()
210 proxy
.__doc
__ = getattr(dict, meth
).__doc
__
211 proxy
.__name
__ = meth
215 values
= _all('values')
216 items
= _all('items')
218 # not available on python 3
220 iterkeys
= _all('iterkeys')
221 itervalues
= _all('itervalues')
222 iteritems
= _all('iteritems')
225 def __contains__(self
, name
):
226 return name
in self
.vars or name
in self
.parent
228 def __getitem__(self
, key
):
229 """Lookup a variable or raise `KeyError` if the variable is
232 item
= self
.resolve(key
)
233 if isinstance(item
, Undefined
):
238 return '<%s %s of %r>' % (
239 self
.__class
__.__name
__,
240 repr(self
.get_all()),
245 # register the context as mapping if possible
247 from collections
import Mapping
248 Mapping
.register(Context
)
253 class BlockReference(object):
254 """One block on a template reference."""
256 def __init__(self
, name
, context
, stack
, depth
):
258 self
._context
= context
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
,
274 rv
= concat(self
._stack
[self
._depth
](self
._context
))
275 if self
._context
.eval_ctx
.autoescape
:
280 class LoopContext(object):
281 """A loop context for dynamic iteration."""
283 def __init__(self
, iterable
, recurse
=None, depth0
=0):
284 self
._iterator
= iter(iterable
)
285 self
._recurse
= recurse
286 self
._after
= self
._safe
_next
()
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!).
295 self
._length
= len(iterable
)
296 except (TypeError, AttributeError):
299 def cycle(self
, *args
):
300 """Cycles among the arguments with the current loop index."""
302 raise TypeError('no items for cycling given')
303 return args
[self
.index0
% len(args
)]
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)
316 return LoopContextIterator(self
)
318 def _safe_next(self
):
320 return next(self
._iterator
)
321 except StopIteration:
322 return _last_iteration
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)
331 # a nifty trick to enhance the error message if someone tried to call
332 # the the loop without or with too many arguments.
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
343 iterable
= tuple(self
._iterator
)
344 self
._iterator
= iter(iterable
)
345 self
._length
= len(iterable
) + self
.index0
+ 1
349 return '<%s %r/%r>' % (
350 self
.__class
__.__name
__,
357 class LoopContextIterator(object):
358 """The iterator for a loop context."""
359 __slots__
= ('context',)
361 def __init__(self
, context
):
362 self
.context
= context
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
378 """Wraps a macro function."""
380 def __init__(self
, environment
, func
, name
, arguments
, defaults
,
381 catch_kwargs
, catch_varargs
, caller
):
382 self
._environment
= environment
384 self
._argument
_count
= len(arguments
)
386 self
.arguments
= arguments
387 self
.defaults
= defaults
388 self
.catch_kwargs
= catch_kwargs
389 self
.catch_varargs
= catch_varargs
393 def __call__(self
, *args
, **kwargs
):
394 # try to consume the positional arguments
395 arguments
= list(args
[:self
._argument
_count
])
398 # if the number of arguments consumed is not the number of
399 # arguments expected we start filling in keyword arguments
401 if off
!= self
._argument
_count
:
402 for idx
, name
in enumerate(self
.arguments
[len(arguments
):]):
404 value
= kwargs
.pop(name
)
407 value
= self
.defaults
[idx
- self
._argument
_count
+ off
]
409 value
= self
._environment
.undefined(
410 'parameter %r was not provided' % name
, name
=name
)
411 arguments
.append(value
)
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!
417 caller
= kwargs
.pop('caller', None)
419 caller
= self
._environment
.undefined('No caller defined',
421 arguments
.append(caller
)
422 if self
.catch_kwargs
:
423 arguments
.append(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
)
436 self
.__class
__.__name
__,
437 self
.name
is None and 'anonymous' or repr(self
.name
)
441 @implements_to_string
442 class 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`:
446 >>> foo = Undefined(name='foo')
452 Traceback (most recent call last):
454 UndefinedError: 'foo' is undefined
456 __slots__
= ('_undefined_hint', '_undefined_obj', '_undefined_name',
457 '_undefined_exception')
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
466 def _fail_with_undefined_error(self
, *args
, **kwargs
):
467 """Regular callback function for undefined objects that raises an
468 `UndefinedError` on call.
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
),
479 hint
= '%r has no attribute %r' % (
480 object_type_repr(self
._undefined
_obj
),
484 hint
= self
._undefined
_hint
485 raise self
._undefined
_exception
(hint
)
488 def __getattr__(self
, name
):
490 raise AttributeError(name
)
491 return self
._fail
_with
_undefined
_error
()
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
500 def __eq__(self
, other
):
501 return type(self
) is type(other
)
503 def __ne__(self
, other
):
504 return not self
.__eq
__(other
)
507 return id(type(self
))
519 def __nonzero__(self
):
526 @implements_to_string
527 class DebugUndefined(Undefined
):
528 """An undefined that returns the debug info when printed.
530 >>> foo = DebugUndefined(name='foo')
536 Traceback (most recent call last):
538 UndefinedError: 'foo' is undefined
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
),
550 return u
'{{ undefined value printed: %s }}' % self
._undefined
_hint
553 @implements_to_string
554 class 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.
559 >>> foo = StrictUndefined(name='foo')
561 Traceback (most recent call last):
563 UndefinedError: 'foo' is undefined
565 Traceback (most recent call last):
567 UndefinedError: 'foo' is undefined
569 Traceback (most recent call last):
571 UndefinedError: 'foo' is undefined
574 __iter__
= __str__
= __len__
= __nonzero__
= __eq__
= \
575 __ne__
= __bool__
= __hash__
= \
576 Undefined
._fail
_with
_undefined
_error
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.
581 del Undefined
.__slots
__, DebugUndefined
.__slots
__, StrictUndefined
.__slots
__