Index: third_party/jinja2/runtime.py |
diff --git a/third_party/jinja2/runtime.py b/third_party/jinja2/runtime.py |
index 7791c645afe997d45e75355fddad384f9d525d94..685a12da068c4808f2dfcec64d68e581f47e26fe 100644 |
--- a/third_party/jinja2/runtime.py |
+++ b/third_party/jinja2/runtime.py |
@@ -8,13 +8,15 @@ |
:copyright: (c) 2010 by the Jinja Team. |
:license: BSD. |
""" |
+import sys |
+ |
from itertools import chain |
from jinja2.nodes import EvalContext, _context_function_types |
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, \ |
+from jinja2._compat import imap, text_type, iteritems, \ |
implements_iterator, implements_to_string, string_types, PY2 |
@@ -22,7 +24,7 @@ from jinja2._compat import next, imap, text_type, iteritems, \ |
__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', |
'TemplateRuntimeError', 'missing', 'concat', 'escape', |
'markup_join', 'unicode_join', 'to_string', 'identity', |
- 'TemplateNotFound'] |
+ 'TemplateNotFound', 'make_logging_undefined'] |
#: the name of the function that is used to convert something into |
#: a string. We can just use the text type here. |
@@ -67,7 +69,8 @@ def new_context(environment, template_name, blocks, vars=None, |
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) |
+ return environment.context_class(environment, parent, template_name, |
+ blocks) |
class TemplateReference(object): |
@@ -171,7 +174,7 @@ class Context(object): |
:func:`environmentfunction`. |
""" |
if __debug__: |
- __traceback_hide__ = True |
+ __traceback_hide__ = True # noqa |
# Allow callable classes to take a context |
fn = __obj.__call__ |
@@ -339,10 +342,11 @@ class LoopContext(object): |
# if was not possible to get the length of the iterator when |
# the loop context was created (ie: iterating over a generator) |
# we have to convert the iterable into a sequence and use the |
- # length of that. |
+ # length of that + the number of iterations so far. |
iterable = tuple(self._iterator) |
self._iterator = iter(iterable) |
- self._length = len(iterable) + self.index0 + 1 |
+ iterations_done = self.index0 + 2 |
+ self._length = len(iterable) + iterations_done |
return self._length |
def __repr__(self): |
@@ -441,7 +445,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`: |
+ iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`: |
>>> foo = Undefined(name='foo') |
>>> str(foo) |
@@ -451,7 +455,7 @@ class Undefined(object): |
>>> foo + 42 |
Traceback (most recent call last): |
... |
- UndefinedError: 'foo' is undefined |
+ jinja2.exceptions.UndefinedError: 'foo' is undefined |
""" |
__slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', |
'_undefined_exception') |
@@ -465,7 +469,7 @@ class Undefined(object): |
@internalcode |
def _fail_with_undefined_error(self, *args, **kwargs): |
"""Regular callback function for undefined objects that raises an |
- `UndefinedError` on call. |
+ `jinja2.exceptions.UndefinedError` on call. |
""" |
if self._undefined_hint is None: |
if self._undefined_obj is missing: |
@@ -491,10 +495,10 @@ class Undefined(object): |
return self._fail_with_undefined_error() |
__add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ |
- __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ |
- __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ |
- __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ |
- __float__ = __complex__ = __pow__ = __rpow__ = \ |
+ __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ |
+ __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ |
+ __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ |
+ __float__ = __complex__ = __pow__ = __rpow__ = \ |
_fail_with_undefined_error |
def __eq__(self, other): |
@@ -518,11 +522,93 @@ class Undefined(object): |
def __nonzero__(self): |
return False |
+ __bool__ = __nonzero__ |
def __repr__(self): |
return 'Undefined' |
+def make_logging_undefined(logger=None, base=None): |
+ """Given a logger object this returns a new undefined class that will |
+ log certain failures. It will log iterations and printing. If no |
+ logger is given a default logger is created. |
+ |
+ Example:: |
+ |
+ logger = logging.getLogger(__name__) |
+ LoggingUndefined = make_logging_undefined( |
+ logger=logger, |
+ base=Undefined |
+ ) |
+ |
+ .. versionadded:: 2.8 |
+ |
+ :param logger: the logger to use. If not provided, a default logger |
+ is created. |
+ :param base: the base class to add logging functionality to. This |
+ defaults to :class:`Undefined`. |
+ """ |
+ if logger is None: |
+ import logging |
+ logger = logging.getLogger(__name__) |
+ logger.addHandler(logging.StreamHandler(sys.stderr)) |
+ if base is None: |
+ base = Undefined |
+ |
+ def _log_message(undef): |
+ if undef._undefined_hint is None: |
+ if undef._undefined_obj is missing: |
+ hint = '%s is undefined' % undef._undefined_name |
+ elif not isinstance(undef._undefined_name, string_types): |
+ hint = '%s has no element %s' % ( |
+ object_type_repr(undef._undefined_obj), |
+ undef._undefined_name) |
+ else: |
+ hint = '%s has no attribute %s' % ( |
+ object_type_repr(undef._undefined_obj), |
+ undef._undefined_name) |
+ else: |
+ hint = undef._undefined_hint |
+ logger.warning('Template variable warning: %s', hint) |
+ |
+ class LoggingUndefined(base): |
+ |
+ def _fail_with_undefined_error(self, *args, **kwargs): |
+ try: |
+ return base._fail_with_undefined_error(self, *args, **kwargs) |
+ except self._undefined_exception as e: |
+ logger.error('Template variable error: %s', str(e)) |
+ raise e |
+ |
+ def __str__(self): |
+ rv = base.__str__(self) |
+ _log_message(self) |
+ return rv |
+ |
+ def __iter__(self): |
+ rv = base.__iter__(self) |
+ _log_message(self) |
+ return rv |
+ |
+ if PY2: |
+ def __nonzero__(self): |
+ rv = base.__nonzero__(self) |
+ _log_message(self) |
+ return rv |
+ |
+ def __unicode__(self): |
+ rv = base.__unicode__(self) |
+ _log_message(self) |
+ return rv |
+ else: |
+ def __bool__(self): |
+ rv = base.__bool__(self) |
+ _log_message(self) |
+ return rv |
+ |
+ return LoggingUndefined |
+ |
+ |
@implements_to_string |
class DebugUndefined(Undefined): |
"""An undefined that returns the debug info when printed. |
@@ -535,7 +621,7 @@ class DebugUndefined(Undefined): |
>>> foo + 42 |
Traceback (most recent call last): |
... |
- UndefinedError: 'foo' is undefined |
+ jinja2.exceptions.UndefinedError: 'foo' is undefined |
""" |
__slots__ = () |
@@ -560,15 +646,15 @@ class StrictUndefined(Undefined): |
>>> str(foo) |
Traceback (most recent call last): |
... |
- UndefinedError: 'foo' is undefined |
+ jinja2.exceptions.UndefinedError: 'foo' is undefined |
>>> not foo |
Traceback (most recent call last): |
... |
- UndefinedError: 'foo' is undefined |
+ jinja2.exceptions.UndefinedError: 'foo' is undefined |
>>> foo + 42 |
Traceback (most recent call last): |
... |
- UndefinedError: 'foo' is undefined |
+ jinja2.exceptions.UndefinedError: 'foo' is undefined |
""" |
__slots__ = () |
__iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ |