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-28 18:49:51 +00:00
|
|
|
import sys
|
2008-04-28 10:20:12 +00:00
|
|
|
from itertools import chain, imap
|
2008-05-03 08:15:31 +00:00
|
|
|
from jinja2.utils import Markup, partial, soft_unicode, escape, missing, concat
|
2008-04-28 10:20:12 +00:00
|
|
|
from jinja2.exceptions import UndefinedError, TemplateRuntimeError
|
2008-04-07 16:39:54 +00:00
|
|
|
|
|
|
|
|
2008-04-26 14:26:52 +00:00
|
|
|
# these variables are exported to the template runtime
|
2008-05-01 10:49:53 +00:00
|
|
|
__all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
|
|
|
|
'TemplateRuntimeError', 'missing', 'concat', 'escape',
|
2008-04-28 10:20:12 +00:00
|
|
|
'markup_join', 'unicode_join']
|
2008-04-25 21:44:14 +00:00
|
|
|
|
|
|
|
|
2008-05-15 20:47:27 +00:00
|
|
|
def markup_join(seq):
|
2008-04-28 10:20:12 +00:00
|
|
|
"""Concatenation that escapes if necessary and converts to unicode."""
|
|
|
|
buf = []
|
2008-05-15 20:47:27 +00:00
|
|
|
iterator = imap(soft_unicode, seq)
|
2008-04-28 10:20:12 +00:00
|
|
|
for arg in iterator:
|
|
|
|
buf.append(arg)
|
|
|
|
if hasattr(arg, '__html__'):
|
|
|
|
return Markup(u'').join(chain(buf, iterator))
|
|
|
|
return concat(buf)
|
|
|
|
|
|
|
|
|
2008-05-15 20:47:27 +00:00
|
|
|
def unicode_join(seq):
|
2008-04-28 10:20:12 +00:00
|
|
|
"""Simple args to unicode conversion and concatenation."""
|
2008-05-15 20:47:27 +00:00
|
|
|
return concat(imap(unicode, seq))
|
2008-04-28 10:20:12 +00:00
|
|
|
|
|
|
|
|
2008-05-01 10:49:53 +00:00
|
|
|
class Context(object):
|
2008-04-30 11:03:59 +00:00
|
|
|
"""The template context holds the variables of a template. It stores the
|
|
|
|
values passed to the template and also the names the template exports.
|
|
|
|
Creating instances is neither supported nor useful as it's created
|
|
|
|
automatically at various stages of the template evaluation and should not
|
|
|
|
be created by hand.
|
|
|
|
|
|
|
|
The context is immutable. Modifications on :attr:`parent` **must not**
|
|
|
|
happen and modifications on :attr:`vars` are allowed from generated
|
|
|
|
template code only. Template filters and global functions marked as
|
|
|
|
:func:`contextfunction`\s get the active context passed as first argument
|
|
|
|
and are allowed to access the context read-only.
|
|
|
|
|
|
|
|
The template context supports read only dict operations (`get`,
|
2008-05-06 14:04:10 +00:00
|
|
|
`keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
|
|
|
|
`__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
|
|
|
|
method that doesn't fail with a `KeyError` but returns an
|
|
|
|
:class:`Undefined` object for missing variables.
|
2008-04-08 16:49:56 +00:00
|
|
|
"""
|
2008-05-18 21:14:14 +00:00
|
|
|
__slots__ = ('parent', 'vars', 'environment', 'exported_vars', 'name',
|
|
|
|
'blocks')
|
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
|
2008-04-26 14:26:52 +00:00
|
|
|
self.vars = 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
|
|
|
|
|
|
|
# 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-27 19:28:03 +00:00
|
|
|
try:
|
|
|
|
blocks = self.blocks[name]
|
2008-05-15 10:22:28 +00:00
|
|
|
block = blocks[blocks.index(current) + 1]
|
2008-04-27 19:28:03 +00:00
|
|
|
except LookupError:
|
2008-04-17 16:44:07 +00:00
|
|
|
return self.environment.undefined('there is no parent block '
|
2008-05-01 10:49:53 +00:00
|
|
|
'called %r.' % name,
|
|
|
|
name='super')
|
2008-04-28 10:20:12 +00:00
|
|
|
wrap = self.environment.autoescape and Markup or (lambda x: x)
|
2008-05-15 10:22:28 +00:00
|
|
|
render = lambda: wrap(concat(block(self)))
|
2008-04-27 19:28:03 +00:00
|
|
|
render.__name__ = render.name = name
|
|
|
|
return render
|
2008-04-07 16:39:54 +00:00
|
|
|
|
2008-04-26 16:30:19 +00:00
|
|
|
def get(self, key, default=None):
|
2008-04-30 11:03:59 +00:00
|
|
|
"""Returns an item from the template context, if it doesn't exist
|
|
|
|
`default` is returned.
|
|
|
|
"""
|
2008-05-06 14:04:10 +00:00
|
|
|
try:
|
|
|
|
return self[key]
|
|
|
|
except KeyError:
|
|
|
|
return default
|
|
|
|
|
|
|
|
def resolve(self, key):
|
|
|
|
"""Looks up a variable like `__getitem__` or `get` but returns an
|
|
|
|
:class:`Undefined` object with the name of the name looked up.
|
|
|
|
"""
|
2008-04-26 16:30:19 +00:00
|
|
|
if key in self.vars:
|
|
|
|
return self.vars[key]
|
|
|
|
if key in self.parent:
|
|
|
|
return self.parent[key]
|
2008-05-06 14:04:10 +00:00
|
|
|
return self.environment.undefined(name=key)
|
2008-04-24 22:36:14 +00:00
|
|
|
|
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."""
|
2008-04-27 19:28:03 +00:00
|
|
|
return dict((k, self.vars[k]) for k in self.exported_vars)
|
2008-04-24 19:54:44 +00:00
|
|
|
|
|
|
|
def get_all(self):
|
2008-04-30 11:03:59 +00:00
|
|
|
"""Return a copy of the complete context as dict including the
|
2008-05-25 09:36:22 +00:00
|
|
|
exported variables.
|
2008-04-30 11:03:59 +00:00
|
|
|
"""
|
2008-04-24 19:54:44 +00:00
|
|
|
return dict(self.parent, **self.vars)
|
|
|
|
|
2008-05-24 22:16:51 +00:00
|
|
|
def call(__self, __obj, *args, **kwargs):
|
|
|
|
"""Called by the template code to inject the current context
|
|
|
|
or environment as first arguments. Then forwards the call to
|
|
|
|
the object with the arguments and keyword arguments.
|
|
|
|
"""
|
|
|
|
if getattr(__obj, 'contextfunction', 0):
|
|
|
|
args = (__self,) + args
|
|
|
|
elif getattr(__obj, 'environmentfunction', 0):
|
|
|
|
args = (__self.environment,) + args
|
|
|
|
return __obj(*args, **kwargs)
|
|
|
|
|
2008-05-06 14:04:10 +00:00
|
|
|
def _all(meth):
|
2008-05-15 14:22:07 +00:00
|
|
|
proxy = lambda self: getattr(self.get_all(), meth)()
|
2008-05-06 14:04:10 +00:00
|
|
|
proxy.__doc__ = getattr(dict, meth).__doc__
|
|
|
|
proxy.__name__ = meth
|
|
|
|
return proxy
|
|
|
|
|
|
|
|
keys = _all('keys')
|
|
|
|
values = _all('values')
|
|
|
|
items = _all('items')
|
|
|
|
iterkeys = _all('iterkeys')
|
|
|
|
itervalues = _all('itervalues')
|
|
|
|
iteritems = _all('iteritems')
|
|
|
|
del _all
|
|
|
|
|
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):
|
2008-05-18 22:23:37 +00:00
|
|
|
"""Lookup a variable or raise `KeyError` if the variable is
|
|
|
|
undefined.
|
|
|
|
"""
|
|
|
|
item = self.resolve(key)
|
|
|
|
if isinstance(item, Undefined):
|
|
|
|
raise KeyError(key)
|
|
|
|
return item
|
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__,
|
2008-04-25 09:44:59 +00:00
|
|
|
repr(self.get_all()),
|
2008-04-17 09:50:39 +00:00
|
|
|
self.name
|
2008-04-11 20:21:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2008-04-27 19:28:03 +00:00
|
|
|
class TemplateReference(object):
|
|
|
|
"""The `self` in templates."""
|
2008-04-13 21:18:05 +00:00
|
|
|
|
2008-04-27 19:28:03 +00:00
|
|
|
def __init__(self, context):
|
|
|
|
self.__context = context
|
2008-04-13 21:18:05 +00:00
|
|
|
|
2008-04-27 19:28:03 +00:00
|
|
|
def __getitem__(self, name):
|
2008-05-23 20:18:38 +00:00
|
|
|
func = self.__context.blocks[name][0]
|
2008-04-28 10:20:12 +00:00
|
|
|
wrap = self.__context.environment.autoescape and \
|
|
|
|
Markup or (lambda x: x)
|
|
|
|
render = lambda: wrap(concat(func(self.__context)))
|
2008-04-27 19:28:03 +00:00
|
|
|
render.__name__ = render.name = name
|
|
|
|
return render
|
2008-04-13 21:18:05 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '<%s %r>' % (
|
|
|
|
self.__class__.__name__,
|
2008-04-27 19:28:03 +00:00
|
|
|
self._context.name
|
2008-04-13 21:18:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2008-04-26 21:21:03 +00:00
|
|
|
class LoopContext(object):
|
|
|
|
"""A loop context for dynamic iteration."""
|
2008-04-09 10:14:24 +00:00
|
|
|
|
2008-05-11 21:21:16 +00:00
|
|
|
def __init__(self, iterable, enforce_length=False, recurse=None):
|
2008-04-09 10:14:24 +00:00
|
|
|
self._iterable = iterable
|
2008-04-26 21:21:03 +00:00
|
|
|
self._next = iter(iterable).next
|
2008-04-09 12:02:55 +00:00
|
|
|
self._length = None
|
2008-05-11 21:21:16 +00:00
|
|
|
self._recurse = recurse
|
2008-04-26 21:21:03 +00:00
|
|
|
self.index0 = -1
|
|
|
|
if enforce_length:
|
|
|
|
len(self)
|
2008-04-09 10:14:24 +00:00
|
|
|
|
2008-04-16 12:21:57 +00:00
|
|
|
def cycle(self, *args):
|
2008-05-18 18:25:28 +00:00
|
|
|
"""Cycles among the arguments with the current loop index."""
|
2008-04-16 12:21:57 +00:00
|
|
|
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
|
|
|
|
|
|
|
def __iter__(self):
|
2008-05-18 18:25:28 +00:00
|
|
|
return LoopContextIterator(self)
|
2008-04-13 11:16:50 +00:00
|
|
|
|
2008-05-11 21:42:19 +00:00
|
|
|
def loop(self, iterable):
|
2008-05-11 21:21:16 +00:00
|
|
|
if self._recurse is None:
|
|
|
|
raise TypeError('Tried to call non recursive loop. Maybe you '
|
2008-05-11 21:42:19 +00:00
|
|
|
"forgot the 'recursive' modifier.")
|
2008-05-11 21:21:16 +00:00
|
|
|
return self._recurse(iterable, self._recurse)
|
|
|
|
|
2008-05-11 21:42:19 +00:00
|
|
|
# a nifty trick to enhance the error message if someone tried to call
|
|
|
|
# the the loop without or with too many arguments.
|
|
|
|
__call__ = loop; del loop
|
|
|
|
|
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:
|
2008-05-18 18:25:28 +00:00
|
|
|
# first try to get the length from the iterable (if the
|
|
|
|
# iterable is a sequence)
|
2008-04-09 10:14:24 +00:00
|
|
|
length = len(self._iterable)
|
|
|
|
except TypeError:
|
2008-05-18 18:25:28 +00:00
|
|
|
# if that's not possible (ie: iterating over a generator)
|
|
|
|
# we have to convert the iterable into a sequence and
|
|
|
|
# use the length of that.
|
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):
|
2008-04-27 19:28:03 +00:00
|
|
|
return '<%s %r/%r>' % (
|
|
|
|
self.__class__.__name__,
|
|
|
|
self.index,
|
|
|
|
self.length
|
|
|
|
)
|
2008-04-16 12:21:57 +00:00
|
|
|
|
2008-04-09 10:14:24 +00:00
|
|
|
|
2008-05-18 18:25:28 +00:00
|
|
|
class LoopContextIterator(object):
|
|
|
|
"""The iterator for a loop context."""
|
|
|
|
__slots__ = ('context',)
|
|
|
|
|
|
|
|
def __init__(self, context):
|
|
|
|
self.context = context
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def next(self):
|
|
|
|
ctx = self.context
|
|
|
|
ctx.index0 += 1
|
|
|
|
return ctx._next(), ctx
|
|
|
|
|
|
|
|
|
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-25 09:44:59 +00:00
|
|
|
def __init__(self, environment, func, name, arguments, defaults,
|
|
|
|
catch_kwargs, catch_varargs, caller):
|
2008-04-14 20:53:58 +00:00
|
|
|
self._environment = environment
|
2008-04-12 12:19:36 +00:00
|
|
|
self._func = func
|
2008-04-29 11:43:16 +00:00
|
|
|
self._argument_count = len(arguments)
|
2008-04-08 16:09:13 +00:00
|
|
|
self.name = name
|
|
|
|
self.arguments = arguments
|
|
|
|
self.defaults = defaults
|
2008-04-25 09:44:59 +00:00
|
|
|
self.catch_kwargs = catch_kwargs
|
|
|
|
self.catch_varargs = catch_varargs
|
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-26 14:26:52 +00:00
|
|
|
arguments = []
|
2008-04-08 16:49:56 +00:00
|
|
|
for idx, name in enumerate(self.arguments):
|
|
|
|
try:
|
|
|
|
value = args[idx]
|
2008-05-01 10:49:53 +00:00
|
|
|
except:
|
2008-04-08 16:49:56 +00:00
|
|
|
try:
|
|
|
|
value = kwargs.pop(name)
|
2008-05-01 10:49:53 +00:00
|
|
|
except:
|
2008-04-08 16:49:56 +00:00
|
|
|
try:
|
2008-04-29 11:43:16 +00:00
|
|
|
value = self.defaults[idx - self._argument_count]
|
2008-05-01 10:49:53 +00:00
|
|
|
except:
|
2008-04-17 16:44:07 +00:00
|
|
|
value = self._environment.undefined(
|
2008-05-01 10:49:53 +00:00
|
|
|
'parameter %r was not provided' % name, name=name)
|
2008-04-26 14:26:52 +00:00
|
|
|
arguments.append(value)
|
|
|
|
|
|
|
|
# it's important that the order of these arguments does not change
|
|
|
|
# if not also changed in the compiler's `function_scoping` method.
|
|
|
|
# the order is caller, keyword arguments, positional arguments!
|
2008-04-12 12:19:36 +00:00
|
|
|
if self.caller:
|
|
|
|
caller = kwargs.pop('caller', None)
|
|
|
|
if caller is None:
|
2008-05-01 10:49:53 +00:00
|
|
|
caller = self._environment.undefined('No caller defined',
|
|
|
|
name='caller')
|
2008-04-26 14:26:52 +00:00
|
|
|
arguments.append(caller)
|
2008-04-25 09:44:59 +00:00
|
|
|
if self.catch_kwargs:
|
2008-04-26 14:26:52 +00:00
|
|
|
arguments.append(kwargs)
|
2008-04-25 09:44:59 +00:00
|
|
|
elif kwargs:
|
|
|
|
raise TypeError('macro %r takes no keyword argument %r' %
|
|
|
|
(self.name, iter(kwargs).next()))
|
|
|
|
if self.catch_varargs:
|
2008-04-29 11:43:16 +00:00
|
|
|
arguments.append(args[self._argument_count:])
|
2008-05-01 10:49:53 +00:00
|
|
|
elif len(args) > self._argument_count:
|
|
|
|
raise TypeError('macro %r takes not more than %d argument(s)' %
|
|
|
|
(self.name, len(self.arguments)))
|
2008-04-26 14:26:52 +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
|
2008-05-07 10:17:18 +00:00
|
|
|
raise self._undefined_exception(hint)
|
2008-04-24 19:54:44 +00:00
|
|
|
|
|
|
|
|
2008-04-09 12:02:55 +00:00
|
|
|
class Undefined(object):
|
2008-04-28 10:20:12 +00:00
|
|
|
"""The default undefined type. This undefined type can be printed and
|
|
|
|
iterated over, but every other access will raise an :exc:`UndefinedError`:
|
|
|
|
|
|
|
|
>>> foo = Undefined(name='foo')
|
|
|
|
>>> str(foo)
|
|
|
|
''
|
|
|
|
>>> not foo
|
|
|
|
True
|
|
|
|
>>> foo + 42
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
|
|
|
jinja2.exceptions.UndefinedError: 'foo' is undefined
|
2008-04-14 20:53:58 +00:00
|
|
|
"""
|
2008-05-07 10:17:18 +00:00
|
|
|
__slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
|
|
|
|
'_undefined_exception')
|
2008-04-09 12:02:55 +00:00
|
|
|
|
2008-05-07 10:17:18 +00:00
|
|
|
def __init__(self, hint=None, obj=None, name=None, exc=UndefinedError):
|
2008-04-17 16:44:07 +00:00
|
|
|
self._undefined_hint = hint
|
|
|
|
self._undefined_obj = obj
|
|
|
|
self._undefined_name = name
|
2008-05-07 10:17:18 +00:00
|
|
|
self._undefined_exception = exc
|
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-26 16:30:19 +00:00
|
|
|
__getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
|
|
|
|
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):
|
2008-04-28 10:20:12 +00:00
|
|
|
"""An undefined that returns the debug info when printed.
|
|
|
|
|
|
|
|
>>> foo = DebugUndefined(name='foo')
|
|
|
|
>>> str(foo)
|
|
|
|
'{{ foo }}'
|
|
|
|
>>> not foo
|
|
|
|
True
|
|
|
|
>>> foo + 42
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
|
|
|
jinja2.exceptions.UndefinedError: 'foo' is undefined
|
|
|
|
"""
|
2008-04-26 16:30:19 +00:00
|
|
|
__slots__ = ()
|
2008-04-14 20:53:58 +00:00
|
|
|
|
|
|
|
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
|
2008-04-26 16:30:19 +00:00
|
|
|
tests and all kinds of comparisons. In other words: you can do nothing
|
|
|
|
with it except checking if it's defined using the `defined` test.
|
2008-04-28 10:20:12 +00:00
|
|
|
|
|
|
|
>>> foo = StrictUndefined(name='foo')
|
|
|
|
>>> str(foo)
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
|
|
|
jinja2.exceptions.UndefinedError: 'foo' is undefined
|
|
|
|
>>> not foo
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
|
|
|
jinja2.exceptions.UndefinedError: 'foo' is undefined
|
|
|
|
>>> foo + 42
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
|
|
|
jinja2.exceptions.UndefinedError: 'foo' is undefined
|
2008-04-17 17:04:44 +00:00
|
|
|
"""
|
2008-04-26 16:30:19 +00:00
|
|
|
__slots__ = ()
|
|
|
|
__iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = __ne__ = \
|
|
|
|
fail_with_undefined_error
|
|
|
|
|
2008-04-14 20:53:58 +00:00
|
|
|
|
2008-04-26 16:30:19 +00:00
|
|
|
# remove remaining slots attributes, after the metaclass did the magic they
|
|
|
|
# are unneeded and irritating as they contain wrong data for the subclasses.
|
|
|
|
del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__
|