2008-04-07 16:39:54 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
jinja2.runtime
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Runtime helpers.
|
|
|
|
|
|
|
|
:copyright: Copyright 2008 by Armin Ronacher.
|
|
|
|
:license: GNU GPL.
|
|
|
|
"""
|
2008-04-22 08:40:26 +00:00
|
|
|
from types import FunctionType
|
|
|
|
from jinja2.utils import Markup, partial
|
2008-04-17 16:44:07 +00:00
|
|
|
from jinja2.exceptions import UndefinedError
|
2008-04-07 16:39:54 +00:00
|
|
|
|
|
|
|
|
2008-04-14 20:53:58 +00:00
|
|
|
__all__ = ['LoopContext', 'StaticLoopContext', 'TemplateContext',
|
2008-04-17 09:23:16 +00:00
|
|
|
'Macro', 'IncludedTemplate', 'Markup']
|
2008-04-10 18:43:43 +00:00
|
|
|
|
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
class TemplateContext(object):
|
2008-04-10 18:43:43 +00:00
|
|
|
"""Holds the variables of the local template or of the global one. It's
|
2008-04-08 16:49:56 +00:00
|
|
|
not save to use this class outside of the compiled code. For example
|
|
|
|
update and other methods will not work as they seem (they don't update
|
|
|
|
the exported variables for example).
|
|
|
|
"""
|
2008-04-07 16:39:54 +00:00
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
def __init__(self, environment, parent, name, blocks):
|
|
|
|
self.parent = parent
|
|
|
|
self.vars = {}
|
2008-04-14 20:53:58 +00:00
|
|
|
self.environment = environment
|
2008-04-24 19:54:44 +00:00
|
|
|
self.exported_vars = set()
|
2008-04-17 09:50:39 +00:00
|
|
|
self.name = name
|
2008-04-24 19:54:44 +00:00
|
|
|
|
|
|
|
# bind functions to the context of environment if required
|
|
|
|
for name, obj in self.parent.iteritems():
|
|
|
|
if type(obj) is FunctionType:
|
|
|
|
if getattr(obj, 'contextfunction', 0):
|
2008-04-24 22:36:14 +00:00
|
|
|
self.vars[name] = partial(obj, self)
|
2008-04-24 19:54:44 +00:00
|
|
|
elif getattr(obj, 'environmentfunction', 0):
|
2008-04-24 22:36:14 +00:00
|
|
|
self.vars[name] = partial(obj, environment)
|
2008-04-24 19:54:44 +00:00
|
|
|
|
|
|
|
# create the initial mapping of blocks. Whenever template inheritance
|
|
|
|
# takes place the runtime will update this mapping with the new blocks
|
|
|
|
# from the template.
|
2008-04-11 11:47:22 +00:00
|
|
|
self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
|
2008-04-11 20:21:00 +00:00
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
def super(self, name, current):
|
2008-04-13 21:18:05 +00:00
|
|
|
"""Render a parent block."""
|
2008-04-24 19:54:44 +00:00
|
|
|
last = None
|
|
|
|
for block in self.blocks[name]:
|
|
|
|
if block is current:
|
|
|
|
break
|
|
|
|
last = block
|
|
|
|
if last is None:
|
2008-04-17 16:44:07 +00:00
|
|
|
return self.environment.undefined('there is no parent block '
|
|
|
|
'called %r.' % block)
|
2008-04-24 19:54:44 +00:00
|
|
|
return SuperBlock(block, self, last)
|
2008-04-07 16:39:54 +00:00
|
|
|
|
2008-04-24 22:36:14 +00:00
|
|
|
def get(self, name, default=None):
|
|
|
|
"""For dict compatibility"""
|
|
|
|
try:
|
|
|
|
return self[name]
|
|
|
|
except KeyError:
|
|
|
|
return default
|
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
def update(self, mapping):
|
|
|
|
"""Update vars from a mapping but don't export them."""
|
|
|
|
self.vars.update(mapping)
|
2008-04-08 16:49:56 +00:00
|
|
|
|
|
|
|
def get_exported(self):
|
2008-04-24 19:54:44 +00:00
|
|
|
"""Get a new dict with the exported variables."""
|
|
|
|
return dict((k, self.vars[k]) for k in self.exported_vars)
|
|
|
|
|
|
|
|
def get_root(self):
|
|
|
|
"""Return a new dict with all the non local variables."""
|
|
|
|
return dict(self.parent)
|
|
|
|
|
|
|
|
def get_all(self):
|
|
|
|
"""Return a copy of the complete context as dict."""
|
|
|
|
return dict(self.parent, **self.vars)
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
self.vars[key] = value
|
|
|
|
self.exported_vars.add(key)
|
|
|
|
|
2008-04-24 22:36:14 +00:00
|
|
|
def __contains__(self, name):
|
|
|
|
return name in self.vars or name in self.parent
|
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
def __getitem__(self, key):
|
|
|
|
if key in self.vars:
|
|
|
|
return self.vars[key]
|
|
|
|
try:
|
|
|
|
return self.parent[key]
|
|
|
|
except KeyError:
|
|
|
|
return self.environment.undefined(name=key)
|
2008-04-08 16:09:13 +00:00
|
|
|
|
2008-04-11 20:21:00 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return '<%s %s of %r>' % (
|
|
|
|
self.__class__.__name__,
|
|
|
|
dict.__repr__(self),
|
2008-04-17 09:50:39 +00:00
|
|
|
self.name
|
2008-04-11 20:21:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2008-04-13 21:18:05 +00:00
|
|
|
class SuperBlock(object):
|
|
|
|
"""When called this renders a parent block."""
|
|
|
|
|
|
|
|
def __init__(self, name, context, render_func):
|
|
|
|
self.name = name
|
|
|
|
self._context = context
|
|
|
|
self._render_func = render_func
|
|
|
|
|
|
|
|
def __call__(self):
|
2008-04-17 09:23:16 +00:00
|
|
|
return Markup(u''.join(self._render_func(self._context)))
|
2008-04-13 21:18:05 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '<%s %r>' % (
|
|
|
|
self.__class__.__name__,
|
|
|
|
self.name
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2008-04-11 20:21:00 +00:00
|
|
|
class IncludedTemplate(object):
|
|
|
|
"""Represents an included template."""
|
|
|
|
|
|
|
|
def __init__(self, environment, context, template):
|
2008-04-11 22:06:19 +00:00
|
|
|
template = environment.get_template(template)
|
2008-04-24 19:54:44 +00:00
|
|
|
context = template.new_context(context.get_root())
|
2008-04-17 09:50:39 +00:00
|
|
|
self._name = template.name
|
2008-04-24 19:54:44 +00:00
|
|
|
self._rendered_body = u''.join(template.root_render_func(context))
|
2008-04-11 22:06:19 +00:00
|
|
|
self._context = context.get_exported()
|
2008-04-11 20:21:00 +00:00
|
|
|
|
2008-04-20 11:11:43 +00:00
|
|
|
__getitem__ = lambda x, n: x._context[n]
|
|
|
|
__html__ = __unicode__ = lambda x: x._rendered_body
|
2008-04-11 20:21:00 +00:00
|
|
|
|
2008-04-11 22:06:19 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return '<%s %r>' % (
|
|
|
|
self.__class__.__name__,
|
2008-04-17 09:50:39 +00:00
|
|
|
self._name
|
2008-04-11 22:06:19 +00:00
|
|
|
)
|
|
|
|
|
2008-04-08 16:09:13 +00:00
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
class LoopContextBase(object):
|
2008-04-09 10:14:24 +00:00
|
|
|
"""Helper for extended iteration."""
|
|
|
|
|
|
|
|
def __init__(self, iterable, parent=None):
|
|
|
|
self._iterable = iterable
|
2008-04-09 12:02:55 +00:00
|
|
|
self._length = None
|
2008-04-09 10:14:24 +00:00
|
|
|
self.index0 = 0
|
|
|
|
self.parent = parent
|
|
|
|
|
2008-04-16 12:21:57 +00:00
|
|
|
def cycle(self, *args):
|
|
|
|
"""A replacement for the old ``{% cycle %}`` tag."""
|
|
|
|
if not args:
|
|
|
|
raise TypeError('no items for cycling given')
|
|
|
|
return args[self.index0 % len(args)]
|
|
|
|
|
2008-04-09 10:14:24 +00:00
|
|
|
first = property(lambda x: x.index0 == 0)
|
|
|
|
last = property(lambda x: x.revindex0 == 0)
|
|
|
|
index = property(lambda x: x.index0 + 1)
|
2008-04-18 09:30:37 +00:00
|
|
|
revindex = property(lambda x: x.length - x.index0)
|
|
|
|
revindex0 = property(lambda x: x.length - x.index)
|
2008-04-16 12:21:57 +00:00
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return self.length
|
2008-04-09 12:02:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
class LoopContext(LoopContextBase):
|
2008-04-11 20:21:00 +00:00
|
|
|
"""A loop context for dynamic iteration."""
|
2008-04-09 12:02:55 +00:00
|
|
|
|
2008-04-18 08:32:14 +00:00
|
|
|
def __init__(self, iterable, enforce_length=False):
|
2008-04-09 12:02:55 +00:00
|
|
|
self._iterable = iterable
|
2008-04-13 11:16:50 +00:00
|
|
|
self._next = iter(iterable).next
|
2008-04-09 12:02:55 +00:00
|
|
|
self._length = None
|
2008-04-13 11:16:50 +00:00
|
|
|
self.index0 = -1
|
2008-04-09 12:02:55 +00:00
|
|
|
if enforce_length:
|
|
|
|
len(self)
|
|
|
|
|
|
|
|
def make_static(self):
|
|
|
|
"""Return a static loop context for the optimizer."""
|
2008-04-18 08:32:14 +00:00
|
|
|
return StaticLoopContext(self.index0, self.length)
|
2008-04-09 12:02:55 +00:00
|
|
|
|
|
|
|
def __iter__(self):
|
2008-04-13 11:16:50 +00:00
|
|
|
return self
|
|
|
|
|
|
|
|
def next(self):
|
|
|
|
self.index0 += 1
|
|
|
|
return self._next(), self
|
2008-04-09 10:14:24 +00:00
|
|
|
|
2008-04-16 12:21:57 +00:00
|
|
|
@property
|
|
|
|
def length(self):
|
2008-04-09 12:02:55 +00:00
|
|
|
if self._length is None:
|
2008-04-09 10:14:24 +00:00
|
|
|
try:
|
|
|
|
length = len(self._iterable)
|
|
|
|
except TypeError:
|
2008-04-09 12:02:55 +00:00
|
|
|
self._iterable = tuple(self._iterable)
|
2008-04-13 11:16:50 +00:00
|
|
|
self._next = iter(self._iterable).next
|
|
|
|
length = len(tuple(self._iterable)) + self.index0 + 1
|
2008-04-09 10:14:24 +00:00
|
|
|
self._length = length
|
|
|
|
return self._length
|
|
|
|
|
2008-04-16 12:21:57 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return 'LoopContext(%r)' % self.index0
|
|
|
|
|
2008-04-09 10:14:24 +00:00
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
class StaticLoopContext(LoopContextBase):
|
2008-04-09 13:03:29 +00:00
|
|
|
"""The static loop context is used in the optimizer to "freeze" the
|
|
|
|
status of an iteration. The only reason for this object is if the
|
|
|
|
loop object is accessed in a non static way (eg: becomes part of a
|
2008-04-18 09:30:37 +00:00
|
|
|
function call).
|
|
|
|
"""
|
2008-04-09 12:02:55 +00:00
|
|
|
|
2008-04-18 08:32:14 +00:00
|
|
|
def __init__(self, index0, length):
|
2008-04-09 12:02:55 +00:00
|
|
|
self.index0 = index0
|
2008-04-16 12:21:57 +00:00
|
|
|
self.length = length
|
2008-04-09 12:02:55 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
2008-04-09 13:03:29 +00:00
|
|
|
"""The repr is used by the optimizer to dump the object."""
|
2008-04-18 08:32:14 +00:00
|
|
|
return 'StaticLoopContext(%r, %r)' % (
|
2008-04-09 12:02:55 +00:00
|
|
|
self.index0,
|
2008-04-18 08:32:14 +00:00
|
|
|
self.length
|
2008-04-09 12:02:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
def make_static(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
2008-04-08 16:09:13 +00:00
|
|
|
class Macro(object):
|
2008-04-09 14:13:39 +00:00
|
|
|
"""Wraps a macro."""
|
2008-04-08 16:09:13 +00:00
|
|
|
|
2008-04-14 20:53:58 +00:00
|
|
|
def __init__(self, environment, func, name, arguments, defaults, catch_all, caller):
|
|
|
|
self._environment = environment
|
2008-04-12 12:19:36 +00:00
|
|
|
self._func = func
|
2008-04-08 16:09:13 +00:00
|
|
|
self.name = name
|
|
|
|
self.arguments = arguments
|
|
|
|
self.defaults = defaults
|
|
|
|
self.catch_all = catch_all
|
2008-04-12 12:19:36 +00:00
|
|
|
self.caller = caller
|
2008-04-08 16:09:13 +00:00
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
2008-04-08 16:49:56 +00:00
|
|
|
arg_count = len(self.arguments)
|
|
|
|
if len(args) > arg_count:
|
2008-04-08 16:09:13 +00:00
|
|
|
raise TypeError('macro %r takes not more than %d argument(s).' %
|
|
|
|
(self.name, len(self.arguments)))
|
|
|
|
arguments = {}
|
2008-04-08 16:49:56 +00:00
|
|
|
for idx, name in enumerate(self.arguments):
|
|
|
|
try:
|
|
|
|
value = args[idx]
|
|
|
|
except IndexError:
|
|
|
|
try:
|
|
|
|
value = kwargs.pop(name)
|
|
|
|
except KeyError:
|
|
|
|
try:
|
|
|
|
value = self.defaults[idx - arg_count]
|
|
|
|
except IndexError:
|
2008-04-17 16:44:07 +00:00
|
|
|
value = self._environment.undefined(
|
|
|
|
'parameter %r was not provided' % name)
|
2008-04-09 13:08:11 +00:00
|
|
|
arguments['l_' + name] = value
|
2008-04-12 12:19:36 +00:00
|
|
|
if self.caller:
|
|
|
|
caller = kwargs.pop('caller', None)
|
|
|
|
if caller is None:
|
2008-04-17 16:44:07 +00:00
|
|
|
caller = self._environment.undefined('No caller defined')
|
2008-04-12 12:19:36 +00:00
|
|
|
arguments['l_caller'] = caller
|
2008-04-08 16:49:56 +00:00
|
|
|
if self.catch_all:
|
|
|
|
arguments['l_arguments'] = kwargs
|
2008-04-13 14:31:08 +00:00
|
|
|
return self._func(**arguments)
|
2008-04-12 12:19:36 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '<%s %s>' % (
|
|
|
|
self.__class__.__name__,
|
|
|
|
self.name is None and 'anonymous' or repr(self.name)
|
|
|
|
)
|
2008-04-09 12:02:55 +00:00
|
|
|
|
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
def fail_with_undefined_error(self, *args, **kwargs):
|
|
|
|
"""Regular callback function for undefined objects that raises an
|
|
|
|
`UndefinedError` on call.
|
|
|
|
"""
|
|
|
|
if self._undefined_hint is None:
|
|
|
|
if self._undefined_obj is None:
|
|
|
|
hint = '%r is undefined' % self._undefined_name
|
|
|
|
elif not isinstance(self._undefined_name, basestring):
|
|
|
|
hint = '%r object has no element %r' % (
|
|
|
|
self._undefined_obj.__class__.__name__,
|
|
|
|
self._undefined_name
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
hint = '%r object has no attribute %r' % (
|
|
|
|
self._undefined_obj.__class__.__name__,
|
|
|
|
self._undefined_name
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
hint = self._undefined_hint
|
|
|
|
raise UndefinedError(hint)
|
|
|
|
|
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
class Undefined(object):
|
2008-04-14 20:53:58 +00:00
|
|
|
"""The default undefined implementation. This undefined implementation
|
|
|
|
can be printed and iterated over, but every other access will raise a
|
|
|
|
`NameError`. Custom undefined classes must subclass this.
|
|
|
|
"""
|
2008-04-09 12:02:55 +00:00
|
|
|
|
2008-04-17 16:44:07 +00:00
|
|
|
def __init__(self, hint=None, obj=None, name=None):
|
|
|
|
self._undefined_hint = hint
|
|
|
|
self._undefined_obj = obj
|
|
|
|
self._undefined_name = name
|
2008-04-09 12:02:55 +00:00
|
|
|
|
2008-04-14 20:53:58 +00:00
|
|
|
__add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
|
|
|
|
__realdiv__ = __rrealdiv__ = __floordiv__ = __rfloordiv__ = \
|
|
|
|
__mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
|
2008-04-24 19:54:44 +00:00
|
|
|
__getattr__ = __getitem__ = fail_with_undefined_error
|
2008-04-14 20:53:58 +00:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.__unicode__().encode('utf-8')
|
2008-04-09 12:02:55 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
2008-04-17 17:04:44 +00:00
|
|
|
return 'Undefined'
|
2008-04-09 12:02:55 +00:00
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
return u''
|
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
def __len__(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
if 0:
|
|
|
|
yield None
|
2008-04-14 20:53:58 +00:00
|
|
|
|
|
|
|
def __nonzero__(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
class DebugUndefined(Undefined):
|
|
|
|
"""An undefined that returns the debug info when printed."""
|
|
|
|
|
|
|
|
def __unicode__(self):
|
2008-04-17 16:44:07 +00:00
|
|
|
if self._undefined_hint is None:
|
|
|
|
if self._undefined_obj is None:
|
|
|
|
return u'{{ %s }}' % self._undefined_name
|
|
|
|
return '{{ no such element: %s[%r] }}' % (
|
|
|
|
self._undefined_obj.__class__.__name__,
|
|
|
|
self._undefined_name
|
|
|
|
)
|
|
|
|
return u'{{ undefined value printed: %s }}' % self._undefined_hint
|
2008-04-14 20:53:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class StrictUndefined(Undefined):
|
2008-04-24 19:54:44 +00:00
|
|
|
"""An undefined that barks on print and iteration as well as boolean
|
|
|
|
tests. In other words: you can do nothing with it except checking if it's
|
|
|
|
defined using the `defined` test.
|
2008-04-17 17:04:44 +00:00
|
|
|
"""
|
2008-04-14 20:53:58 +00:00
|
|
|
|
2008-04-24 19:54:44 +00:00
|
|
|
__iter__ = __unicode__ = __len__ = __nonzero__ = fail_with_undefined_error
|