2008-04-07 16:39:54 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
jinja2.runtime
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Runtime helpers.
|
|
|
|
|
|
|
|
:copyright: Copyright 2008 by Armin Ronacher.
|
|
|
|
:license: GNU GPL.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
from collections import defaultdict
|
|
|
|
except ImportError:
|
|
|
|
defaultdict = None
|
|
|
|
|
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
# contains only the variables the template will import automatically, not the
|
|
|
|
# objects injected by the evaluation loop (such as undefined objects)
|
|
|
|
__all__ = ['extends', 'subscribe', 'LoopContext', 'StaticLoopContext',
|
|
|
|
'TemplateContext', 'Macro']
|
2008-04-07 16:39:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
def extends(template, namespace):
|
2008-04-08 12:47:40 +00:00
|
|
|
"""This loads a template (and evaluates it) and replaces the blocks."""
|
|
|
|
|
|
|
|
|
|
|
|
def subscribe(obj, argument, undefined_factory):
|
|
|
|
"""Get an item or attribute of an object."""
|
|
|
|
try:
|
2008-04-08 22:40:05 +00:00
|
|
|
return getattr(obj, str(argument))
|
|
|
|
except (AttributeError, UnicodeError):
|
2008-04-08 12:47:40 +00:00
|
|
|
try:
|
|
|
|
return obj[argument]
|
|
|
|
except LookupError:
|
|
|
|
return undefined_factory(attr=argument)
|
2008-04-07 16:39:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TemplateContext(dict):
|
2008-04-08 16:49:56 +00:00
|
|
|
"""
|
|
|
|
Holds the variables of the local template or of the global one. It's
|
|
|
|
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
|
|
|
|
|
|
|
def __init__(self, globals, undefined_factory, filename):
|
2008-04-08 16:49:56 +00:00
|
|
|
dict.__init__(self, globals)
|
|
|
|
self.exported = set()
|
2008-04-07 16:39:54 +00:00
|
|
|
self.undefined_factory = undefined_factory
|
|
|
|
self.filename = filename
|
2008-04-08 12:47:40 +00:00
|
|
|
self.filters = {}
|
|
|
|
self.tests = {}
|
2008-04-07 16:39:54 +00:00
|
|
|
|
2008-04-08 16:49:56 +00:00
|
|
|
def __setitem__(self, key, value):
|
|
|
|
"""If we set items to the dict we track the variables set so
|
|
|
|
that includes can access the exported variables."""
|
|
|
|
dict.__setitem__(self, key, value)
|
|
|
|
self.exported.add(key)
|
|
|
|
|
|
|
|
def __delitem__(self, key):
|
|
|
|
"""On delete we no longer export it."""
|
|
|
|
dict.__delitem__(self, key)
|
|
|
|
self.exported.dicard(key)
|
|
|
|
|
|
|
|
def get_exported(self):
|
|
|
|
"""Get a dict of all exported variables."""
|
|
|
|
return dict((k, self[k]) for k in self.exported)
|
|
|
|
|
2008-04-07 16:39:54 +00:00
|
|
|
# if there is a default dict, dict has a __missing__ method we can use.
|
|
|
|
if defaultdict is None:
|
|
|
|
def __getitem__(self, name):
|
|
|
|
if name in self:
|
|
|
|
return self[name]
|
|
|
|
return self.undefined_factory(name)
|
|
|
|
else:
|
|
|
|
def __missing__(self, key):
|
2008-04-08 16:49:56 +00:00
|
|
|
return self.undefined_factory(key)
|
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
|
|
|
|
|
|
|
|
first = property(lambda x: x.index0 == 0)
|
|
|
|
last = property(lambda x: x.revindex0 == 0)
|
|
|
|
index = property(lambda x: x.index0 + 1)
|
|
|
|
revindex = property(lambda x: x.length)
|
|
|
|
revindex0 = property(lambda x: x.length - 1)
|
2008-04-09 12:02:55 +00:00
|
|
|
length = property(lambda x: len(x))
|
|
|
|
|
|
|
|
|
|
|
|
class LoopContext(LoopContextBase):
|
|
|
|
|
|
|
|
def __init__(self, iterable, parent=None, enforce_length=False):
|
|
|
|
self._iterable = iterable
|
|
|
|
self._length = None
|
|
|
|
self.index0 = 0
|
|
|
|
self.parent = parent
|
|
|
|
if enforce_length:
|
|
|
|
len(self)
|
|
|
|
|
|
|
|
def make_static(self):
|
|
|
|
"""Return a static loop context for the optimizer."""
|
|
|
|
parent = None
|
|
|
|
if self.parent is not None:
|
|
|
|
parent = self.parent.make_static()
|
|
|
|
return StaticLoopContext(self.index0, self.length, parent)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
for item in self._iterable:
|
|
|
|
yield self, item
|
|
|
|
self.index0 += 1
|
2008-04-09 10:14:24 +00:00
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
def __len__(self):
|
|
|
|
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)
|
|
|
|
length = self.index0 + len(tuple(self._iterable))
|
2008-04-09 10:14:24 +00:00
|
|
|
self._length = length
|
|
|
|
return self._length
|
|
|
|
|
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
class StaticLoopContext(LoopContextBase):
|
|
|
|
|
|
|
|
def __init__(self, index0, length, parent):
|
|
|
|
self.index0 = index0
|
|
|
|
self.parent = parent
|
|
|
|
self._length = length
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return 'StaticLoopContext(%r, %r, %r)' % (
|
|
|
|
self.index0,
|
|
|
|
self._length,
|
|
|
|
self.parent
|
|
|
|
)
|
|
|
|
|
|
|
|
def make_static(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
2008-04-08 16:09:13 +00:00
|
|
|
class Macro(object):
|
2008-04-08 16:49:56 +00:00
|
|
|
"""
|
|
|
|
Wraps a macor
|
|
|
|
"""
|
2008-04-08 16:09:13 +00:00
|
|
|
|
2008-04-08 16:49:56 +00:00
|
|
|
def __init__(self, func, name, arguments, defaults, catch_all, \
|
|
|
|
undefined_factory):
|
2008-04-08 16:09:13 +00:00
|
|
|
self.func = func
|
|
|
|
self.name = name
|
|
|
|
self.arguments = arguments
|
|
|
|
self.defaults = defaults
|
|
|
|
self.catch_all = catch_all
|
2008-04-08 16:49:56 +00:00
|
|
|
self.undefined_factory = undefined_factory
|
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:
|
|
|
|
value = self.undefined_factory(name)
|
|
|
|
arguments['l_' + name] = arg
|
|
|
|
if self.catch_all:
|
|
|
|
arguments['l_arguments'] = kwargs
|
|
|
|
return u''.join(self.func(**arguments))
|
2008-04-09 12:02:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Undefined(object):
|
|
|
|
"""The default undefined behavior."""
|
|
|
|
|
|
|
|
def __init__(self, name=None, attr=None):
|
|
|
|
if attr is None:
|
|
|
|
self._undefined_hint = '%r is undefined' % attr
|
|
|
|
elif name is None:
|
|
|
|
self._undefined_hint = 'attribute %r is undefined' % name
|
|
|
|
else:
|
|
|
|
self._undefined_hint = 'attribute %r of %r is undefined' \
|
|
|
|
% (attr, name)
|
|
|
|
|
|
|
|
def fail(self, *args, **kwargs):
|
|
|
|
raise TypeError(self._undefined_hint)
|
|
|
|
__getattr__ = __getitem__ = __add__ = __mul__ = __div__ = \
|
|
|
|
__realdiv__ = __floordiv__ = __mod__ = __pos__ = __neg__ = fail
|
|
|
|
del fail
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return ''
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return 'Undefined'
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
if 0:
|
|
|
|
yield None
|