]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Added tests for logging undefined and added it to the docs.
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 6 Jun 2014 16:14:45 +0000 (22:14 +0600)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 6 Jun 2014 16:14:45 +0000 (22:14 +0600)
CHANGES
docs/api.rst
jinja2/__init__.py
jinja2/runtime.py
jinja2/testsuite/api.py

diff --git a/CHANGES b/CHANGES
index 907e1c062c5c30f3f5bcace9583cf3405a501c58..b9606f6e8b12e0147e7ee51031b1d4e8b396fad9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -14,6 +14,8 @@ Version 2.8
 - Fixed loop length calculation for some iterators.
 - Changed how Jinja2 enforces strings to be native strings in
   Python 2 to work when people break their default encoding.
+- Added :func:`make_logging_undefined` which returns an undefined
+  object that logs failures into a logger.
 
 Version 2.7.2
 -------------
index ae2295fdb53aad0015fad47c07555a2f6e814f1f..1fd02c655cd0bc2c0ae37372aa3ab59bda88c963 100644 (file)
@@ -316,6 +316,11 @@ disallows all operations beside testing if it's an undefined object.
 
 .. autoclass:: jinja2.StrictUndefined()
 
+There is also a factory function that can decorate undefined objects to
+implement logging on failures:
+
+.. autofunction:: jinja2.make_logging_undefined
+
 Undefined objects are created by calling :attr:`undefined`.
 
 .. admonition:: Implementation
index 8434dea18000532d473a89e90b4d38b212a434f7..f67fa91e35891bd0ec72cdccef5eb8057c83c871 100644 (file)
@@ -42,7 +42,8 @@ from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
      MemcachedBytecodeCache
 
 # undefined types
-from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
+from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined, \
+     make_logging_undefined
 
 # exceptions
 from jinja2.exceptions import TemplateError, UndefinedError, \
@@ -65,5 +66,5 @@ __all__ = [
     'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
     'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
     'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
-    'evalcontextfilter', 'evalcontextfunction'
+    'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined',
 ]
index 65476c43270d20f75e98e09e746f46267ee95018..937e8674b0b8296a40c2c5511cb447e39af825b9 100644 (file)
@@ -527,9 +527,24 @@ class Undefined(object):
 
 
 def make_logging_undefined(logger=None, base=None):
-    """Given a logger object this returns a new undefined class that
-    will log all failures into it.  If no logger is given a default
-    logger is created.
+    """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
@@ -538,23 +553,51 @@ def make_logging_undefined(logger=None, base=None):
     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):
-            if self._undefined_hint is None:
-                if self._undefined_obj is missing:
-                    hint = '%s is undefined' % self._undefined_name
-                elif not isinstance(self._undefined_name, string_types):
-                    hint = '%s has no element %s' % (
-                        object_type_repr(self._undefined_obj),
-                        self._undefined_name)
-                else:
-                    hint = '%s has no attribute %s' % (
-                        object_type_repr(self._undefined_obj),
-                        self._undefined_name)
-            else:
-                hint = self._undefined_hint
-            logger.error('Template error: %s', hint)
-            return base.__str__(self)
+            rv = base.__str__(self)
+            _log_message(self)
+            return rv
+
+        if PY2:
+            def __unicode__(self):
+                rv = base.__unicode__(self)
+                _log_message(self)
+                return rv
+
+        def __iter__(self):
+            rv = base.__iter__(self)
+            _log_message(self)
+            return rv
+
+        def __nonzero__(self):
+            rv = base.__nonzero__(self)
+            _log_message(self)
+            return rv
 
     return LoggingUndefined
 
index 4a0ec5a9bd60fcecb8258d44c1b88eb6b63378d8..ea3739bf4965e1ea2b8e2005199f17a08e9a4b53 100644 (file)
@@ -17,7 +17,7 @@ from jinja2.testsuite import JinjaTestCase
 
 from jinja2 import Environment, Undefined, DebugUndefined, \
      StrictUndefined, UndefinedError, meta, \
-     is_undefined, Template, DictLoader
+     is_undefined, Template, DictLoader, make_logging_undefined
 from jinja2.utils import Cycler
 
 env = Environment()
@@ -199,6 +199,31 @@ class UndefinedTestCase(JinjaTestCase):
         else:
             assert False, "Expected actual attribute error"
 
+    def test_logging_undefined(self):
+        _messages = []
+        class DebugLogger(object):
+            def warning(self, msg, *args):
+                _messages.append('W:' + msg % args)
+            def error(self, msg, *args):
+                _messages.append('E:' + msg % args)
+
+        logging_undefined = make_logging_undefined(DebugLogger())
+        env = Environment(undefined=logging_undefined)
+        self.assert_equal(env.from_string('{{ missing }}').render(), u'')
+        self.assert_raises(UndefinedError,
+                           env.from_string('{{ missing.attribute }}').render)
+        self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
+        self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
+        self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '')
+        self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
+        self.assert_equal(_messages, [
+            'W:Template variable warning: missing is undefined',
+            "E:Template variable error: 'missing' is undefined",
+            'W:Template variable warning: missing is undefined',
+            'W:Template variable warning: int object has no attribute missing',
+            'W:Template variable warning: missing is undefined',
+        ])
+
     def test_default_undefined(self):
         env = Environment(undefined=Undefined)
         self.assert_equal(env.from_string('{{ missing }}').render(), u'')