]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Add support for a {% debug %} extension tag.
authorShaheed Haque <srhaque@theiet.org>
Sun, 14 Jan 2018 14:01:22 +0000 (14:01 +0000)
committerDavid Lord <davidism@gmail.com>
Fri, 4 Oct 2019 20:15:20 +0000 (13:15 -0700)
This dumps the available variables, filters and tests.

jinja2/ext.py
tests/test_ext.py

index eed3f21025ec55672b2401a9eaaea0ffbab1d297..6e21570a246909289acd383ba992e38989bc0f6b 100644 (file)
@@ -4,13 +4,19 @@
     ~~~~~~~~~~
 
     Jinja extensions allow to add custom tags similar to the way django custom
-    tags work.  By default two example extensions exist: an i18n and a cache
-    extension.
+    tags work.  The following default example extensions are included:
+
+        - a i18n support {% trans %} tag
+        - a {% do %} tag
+        - loop control {% break %} and {% continue %} tags
+        - a {% debug %} tag
 
     :copyright: (c) 2017 by the Jinja Team.
     :license: BSD.
 """
+import pprint
 import re
+from sys import version_info
 
 from jinja2 import nodes
 from jinja2.defaults import BLOCK_START_STRING, \
@@ -19,10 +25,12 @@ from jinja2.defaults import BLOCK_START_STRING, \
      LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
      KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
 from jinja2.environment import Environment
+from jinja2.nodes import ContextReference
 from jinja2.runtime import concat
 from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
 from jinja2.utils import contextfunction, import_string, Markup
 from jinja2._compat import with_metaclass, string_types, iteritems
+from markupsafe import escape
 
 
 # the only real useful gettext functions for a Jinja template.  Note
@@ -434,6 +442,59 @@ class AutoEscapeExtension(Extension):
     pass
 
 
+class DebugExtension(Extension):
+    """
+    A ``{% debug %}`` tag that dumps the available variables, filters and tests.
+    Typical usage like this:
+
+    .. codeblock:: html+jinja
+        <pre>{% debug %}</pre>
+
+    produces output like this:
+
+    ::
+        {'context': {'_': <function _gettext_alias at 0x7f9ceabde488>,
+                 'csrf_token': <SimpleLazyObject: 'lfPE7al...q3bykS4txKfb3'>,
+                 'cycler': <class 'jinja2.utils.Cycler'>,
+                 ...
+                 'view': <polls.views_auth.Login object at 0x7f9cea2cbe48>},
+        'filters': ['abs', 'add', 'addslashes', 'attr', 'batch', 'bootstrap',
+                 'bootstrap_classes', 'bootstrap_horizontal',
+                 'bootstrap_inline', ... 'yesno'],
+        'tests': ['callable', 'checkbox_field', 'defined', 'divisibleby',
+               'escaped', 'even', 'iterable', 'lower', 'mapping',
+               'multiple_checkbox_field', ... 'string', 'undefined', 'upper']}
+
+    """
+    tags = {'debug'}
+
+    def __init__(self, environment):
+        super(DebugExtension, self).__init__(environment)
+
+    def parse(self, parser):
+        lineno = parser.stream.expect('name:debug').lineno
+        context = ContextReference()
+        call = self.call_method('_render', [context], lineno=lineno)
+        return nodes.Output([nodes.MarkSafe(call)])
+
+    def _render(self, context):
+        result = {
+            'filters': sorted(self.environment.filters.keys()),
+            'tests': sorted(self.environment.tests.keys()),
+            'context': context.get_all()
+        }
+        #
+        # We set the depth since the intent is basically to show the top few
+        # names. TODO: provide user control over this?
+        #
+        if version_info[:2] >= (3,4):
+            text = pprint.pformat(result, depth=3, compact=True)
+        else:
+            text = pprint.pformat(result, depth=3)
+        text = escape(text)
+        return text
+
+
 def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
                      babel_style=True):
     """Extract localizable strings from the given template node.  Per
@@ -625,3 +686,4 @@ do = ExprStmtExtension
 loopcontrols = LoopControlExtension
 with_ = WithExtension
 autoescape = AutoEscapeExtension
+debug = DebugExtension
index c3b028ff9553f1922140699587244af6a8e20443..1f26373df316a3df13fbf67dd7cf3f63670de962 100644 (file)
@@ -240,6 +240,22 @@ class TestExtensions(object):
         assert ext[0].__class__ is T1
         assert ext[1].__class__ is T2
 
+    def test_debug(self):
+        """Test for {% debug %}"""
+        env = Environment(extensions=['jinja2.ext.debug'])
+        tmpl = env.from_string('''Hello{% debug %}Bye''')
+        out = tmpl.render()
+        out = out.replace('&#39;', "'").replace('&lt;', '<').replace('&gt;', '>')
+        #
+        # Check that some of the built-in items exist in the debug output...
+        #
+        assert "'context'" in out
+        assert "'cycler'" in out
+        assert "'filters'" in out
+        assert "'abs'" in out
+        assert "'tests'" in out
+        assert "'!='" in out
+
 
 @pytest.mark.ext
 class TestInternationalization(object):