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 |