| Index: third_party/jinja2/runtime.py
|
| diff --git a/third_party/jinja2/runtime.py b/third_party/jinja2/runtime.py
|
| index a4a47a284ed4a51ec9e54ae9b439682884e1905c..7791c645afe997d45e75355fddad384f9d525d94 100644
|
| --- a/third_party/jinja2/runtime.py
|
| +++ b/third_party/jinja2/runtime.py
|
| @@ -8,12 +8,14 @@
|
| :copyright: (c) 2010 by the Jinja Team.
|
| :license: BSD.
|
| """
|
| -from itertools import chain, imap
|
| +from itertools import chain
|
| from jinja2.nodes import EvalContext, _context_function_types
|
| -from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
|
| - concat, internalcode, next, object_type_repr
|
| +from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \
|
| + internalcode, object_type_repr
|
| from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
|
| TemplateNotFound
|
| +from jinja2._compat import next, imap, text_type, iteritems, \
|
| + implements_iterator, implements_to_string, string_types, PY2
|
|
|
|
|
| # these variables are exported to the template runtime
|
| @@ -23,13 +25,14 @@ __all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
|
| 'TemplateNotFound']
|
|
|
| #: the name of the function that is used to convert something into
|
| -#: a string. 2to3 will adopt that automatically and the generated
|
| -#: code can take advantage of it.
|
| -to_string = unicode
|
| +#: a string. We can just use the text type here.
|
| +to_string = text_type
|
|
|
| #: the identity function. Useful for certain things in the environment
|
| identity = lambda x: x
|
|
|
| +_last_iteration = object()
|
| +
|
|
|
| def markup_join(seq):
|
| """Concatenation that escapes if necessary and converts to unicode."""
|
| @@ -44,7 +47,7 @@ def markup_join(seq):
|
|
|
| def unicode_join(seq):
|
| """Simple args to unicode conversion and concatenation."""
|
| - return concat(imap(unicode, seq))
|
| + return concat(imap(text_type, seq))
|
|
|
|
|
| def new_context(environment, template_name, blocks, vars=None,
|
| @@ -61,7 +64,7 @@ def new_context(environment, template_name, blocks, vars=None,
|
| # we don't want to modify the dict passed
|
| if shared:
|
| parent = dict(parent)
|
| - for key, value in locals.iteritems():
|
| + for key, value in iteritems(locals):
|
| if key[:2] == 'l_' and value is not missing:
|
| parent[key[2:]] = value
|
| return Context(environment, parent, template_name, blocks)
|
| @@ -117,7 +120,7 @@ class Context(object):
|
| # create the initial mapping of blocks. Whenever template inheritance
|
| # takes place the runtime will update this mapping with the new blocks
|
| # from the template.
|
| - self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
|
| + self.blocks = dict((k, [v]) for k, v in iteritems(blocks))
|
|
|
| def super(self, name, current):
|
| """Render a parent block."""
|
| @@ -169,6 +172,16 @@ class Context(object):
|
| """
|
| if __debug__:
|
| __traceback_hide__ = True
|
| +
|
| + # Allow callable classes to take a context
|
| + fn = __obj.__call__
|
| + for fn_type in ('contextfunction',
|
| + 'evalcontextfunction',
|
| + 'environmentfunction'):
|
| + if hasattr(fn, fn_type):
|
| + __obj = fn
|
| + break
|
| +
|
| if isinstance(__obj, _context_function_types):
|
| if getattr(__obj, 'contextfunction', 0):
|
| args = (__self,) + args
|
| @@ -189,7 +202,7 @@ class Context(object):
|
| self.parent, True, None, locals)
|
| context.vars.update(self.vars)
|
| context.eval_ctx = self.eval_ctx
|
| - context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
|
| + context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks))
|
| return context
|
|
|
| def _all(meth):
|
| @@ -203,7 +216,7 @@ class Context(object):
|
| items = _all('items')
|
|
|
| # not available on python 3
|
| - if hasattr(dict, 'iterkeys'):
|
| + if PY2:
|
| iterkeys = _all('iterkeys')
|
| itervalues = _all('itervalues')
|
| iteritems = _all('iteritems')
|
| @@ -267,10 +280,12 @@ class BlockReference(object):
|
| class LoopContext(object):
|
| """A loop context for dynamic iteration."""
|
|
|
| - def __init__(self, iterable, recurse=None):
|
| + def __init__(self, iterable, recurse=None, depth0=0):
|
| self._iterator = iter(iterable)
|
| self._recurse = recurse
|
| + self._after = self._safe_next()
|
| self.index0 = -1
|
| + self.depth0 = depth0
|
|
|
| # try to get the length of the iterable early. This must be done
|
| # here because there are some broken iterators around where there
|
| @@ -288,10 +303,11 @@ class LoopContext(object):
|
| return args[self.index0 % len(args)]
|
|
|
| first = property(lambda x: x.index0 == 0)
|
| - last = property(lambda x: x.index0 + 1 == x.length)
|
| + last = property(lambda x: x._after is _last_iteration)
|
| index = property(lambda x: x.index0 + 1)
|
| revindex = property(lambda x: x.length - x.index0)
|
| revindex0 = property(lambda x: x.length - x.index)
|
| + depth = property(lambda x: x.depth0 + 1)
|
|
|
| def __len__(self):
|
| return self.length
|
| @@ -299,12 +315,18 @@ class LoopContext(object):
|
| def __iter__(self):
|
| return LoopContextIterator(self)
|
|
|
| + def _safe_next(self):
|
| + try:
|
| + return next(self._iterator)
|
| + except StopIteration:
|
| + return _last_iteration
|
| +
|
| @internalcode
|
| def loop(self, iterable):
|
| if self._recurse is None:
|
| raise TypeError('Tried to call non recursive loop. Maybe you '
|
| "forgot the 'recursive' modifier.")
|
| - return self._recurse(iterable, self._recurse)
|
| + return self._recurse(iterable, self._recurse, self.depth0 + 1)
|
|
|
| # a nifty trick to enhance the error message if someone tried to call
|
| # the the loop without or with too many arguments.
|
| @@ -331,6 +353,7 @@ class LoopContext(object):
|
| )
|
|
|
|
|
| +@implements_iterator
|
| class LoopContextIterator(object):
|
| """The iterator for a loop context."""
|
| __slots__ = ('context',)
|
| @@ -341,10 +364,14 @@ class LoopContextIterator(object):
|
| def __iter__(self):
|
| return self
|
|
|
| - def next(self):
|
| + def __next__(self):
|
| ctx = self.context
|
| ctx.index0 += 1
|
| - return next(ctx._iterator), ctx
|
| + if ctx._after is _last_iteration:
|
| + raise StopIteration()
|
| + next_elem = ctx._after
|
| + ctx._after = ctx._safe_next()
|
| + return next_elem, ctx
|
|
|
|
|
| class Macro(object):
|
| @@ -411,6 +438,7 @@ class Macro(object):
|
| )
|
|
|
|
|
| +@implements_to_string
|
| class Undefined(object):
|
| """The default undefined type. This undefined type can be printed and
|
| iterated over, but every other access will raise an :exc:`UndefinedError`:
|
| @@ -442,7 +470,7 @@ class Undefined(object):
|
| if self._undefined_hint is None:
|
| if self._undefined_obj is missing:
|
| hint = '%r is undefined' % self._undefined_name
|
| - elif not isinstance(self._undefined_name, basestring):
|
| + elif not isinstance(self._undefined_name, string_types):
|
| hint = '%s has no element %r' % (
|
| object_type_repr(self._undefined_obj),
|
| self._undefined_name
|
| @@ -469,14 +497,16 @@ class Undefined(object):
|
| __float__ = __complex__ = __pow__ = __rpow__ = \
|
| _fail_with_undefined_error
|
|
|
| - def __str__(self):
|
| - return unicode(self).encode('utf-8')
|
| + def __eq__(self, other):
|
| + return type(self) is type(other)
|
| +
|
| + def __ne__(self, other):
|
| + return not self.__eq__(other)
|
| +
|
| + def __hash__(self):
|
| + return id(type(self))
|
|
|
| - # unicode goes after __str__ because we configured 2to3 to rename
|
| - # __unicode__ to __str__. because the 2to3 tree is not designed to
|
| - # remove nodes from it, we leave the above __str__ around and let
|
| - # it override at runtime.
|
| - def __unicode__(self):
|
| + def __str__(self):
|
| return u''
|
|
|
| def __len__(self):
|
| @@ -493,6 +523,7 @@ class Undefined(object):
|
| return 'Undefined'
|
|
|
|
|
| +@implements_to_string
|
| class DebugUndefined(Undefined):
|
| """An undefined that returns the debug info when printed.
|
|
|
| @@ -508,7 +539,7 @@ class DebugUndefined(Undefined):
|
| """
|
| __slots__ = ()
|
|
|
| - def __unicode__(self):
|
| + def __str__(self):
|
| if self._undefined_hint is None:
|
| if self._undefined_obj is missing:
|
| return u'{{ %s }}' % self._undefined_name
|
| @@ -519,6 +550,7 @@ class DebugUndefined(Undefined):
|
| return u'{{ undefined value printed: %s }}' % self._undefined_hint
|
|
|
|
|
| +@implements_to_string
|
| class StrictUndefined(Undefined):
|
| """An undefined that barks on print and iteration as well as boolean
|
| tests and all kinds of comparisons. In other words: you can do nothing
|
| @@ -539,8 +571,9 @@ class StrictUndefined(Undefined):
|
| UndefinedError: 'foo' is undefined
|
| """
|
| __slots__ = ()
|
| - __iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \
|
| - __ne__ = __bool__ = Undefined._fail_with_undefined_error
|
| + __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \
|
| + __ne__ = __bool__ = __hash__ = \
|
| + Undefined._fail_with_undefined_error
|
|
|
|
|
| # remove remaining slots attributes, after the metaclass did the magic they
|
|
|