[svn] improved jinja debugging system. now handles errors in included templates too
authorArmin Ronacher <armin.ronacher@active-4.com>
Wed, 14 Mar 2007 19:41:27 +0000 (20:41 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Wed, 14 Mar 2007 19:41:27 +0000 (20:41 +0100)
--HG--
branch : trunk

docs/src/objects.txt
jinja/translators/python.py
jinja/utils.py
tests/exception.py
tests/templates/error.html
tests/templates/included_error.html [new file with mode: 0644]
www/static/pygments.css

index 357a3d681a23d2696391508f36efe5bbec36a4fc..5d0bddce934eaafb97fe4948c0242907eaab60ec 100644 (file)
@@ -87,6 +87,10 @@ access. You can mark both attributes and methods as unsafe:
     class MyModel(...):
 
         # just give access to a and b. Default is all
+        # note that this also disallows the functions from below.
+        # if you use jinja_allowed_attributes you don't have to
+        # do the code below because methods are threaded as attributes
+        # too.
         jinja_allowed_attributes = ['a', 'b']
 
         def __init__(self, ...):
index 6e2ef018bd9a62a53de5bdeb0c4a2ec47ef10491..b341852d5c7f5c47540726725f440792d3eb5021 100644 (file)
@@ -39,11 +39,6 @@ class Template(object):
         self.translated_source = translated_source
         self.generate_func = None
 
-    def source(self):
-        """The original sourcecode for this template."""
-        return self.environment.loader.get_source(self.code.co_filename)
-    source = property(source, doc=source.__doc__)
-
     def dump(self, stream=None):
         """Dump the template into python bytecode."""
         if stream is not None:
@@ -175,12 +170,16 @@ class PythonTranslator(Translator):
 
     def nodeinfo(self, node):
         """
-        Return a comment that helds the node informations.
+        Return a comment that helds the node informations or None
+        if there is no need to add a debug comment.
         """
-        return '# DEBUG(filename=%r, lineno=%s)' % (
+        rv = '# DEBUG(filename=%s, lineno=%s)' % (
             node.filename,
             node.lineno
         )
+        if rv != self.last_debug_comment:
+            self.last_debug_comment = rv
+            return rv
 
     def filter(self, s, filter_nodes):
         """
@@ -235,6 +234,7 @@ class PythonTranslator(Translator):
     def reset(self):
         self.indention = 0
         self.last_cycle_id = 0
+        self.last_debug_comment = None
 
     def translate(self):
         self.reset()
@@ -329,8 +329,10 @@ class PythonTranslator(Translator):
         """
         Handle data around nodes.
         """
-        return self.indent(self.nodeinfo(node)) + '\n' + \
-               self.indent('yield %r' % node.text)
+        nodeinfo = self.nodeinfo(node) or ''
+        if nodeinfo:
+            nodeinfo = self.indent(nodeinfo) + '\n'
+        return nodeinfo + self.indent('yield %r' % node.text)
 
     def handle_node_list(self, node):
         """
@@ -339,8 +341,10 @@ class PythonTranslator(Translator):
         """
         buf = [self.handle_node(n) for n in node]
         if buf:
-            return '\n'.join([self.indent(self.nodeinfo(node))] + buf)
-        return ''
+            nodeinfo = self.nodeinfo(node)
+            if nodeinfo:
+                buf = [self.indent(nodeinfo)] + buf
+        return '\n'.join(buf)
 
     def handle_for_loop(self, node):
         """
@@ -349,7 +353,9 @@ class PythonTranslator(Translator):
         """
         buf = []
         write = lambda x: buf.append(self.indent(x))
-        write(self.nodeinfo(node))
+        nodeinfo = self.nodeinfo(node)
+        if nodeinfo:
+            write(nodeinfo)
         write('ctx_push()')
 
         # recursive loops
@@ -370,7 +376,9 @@ class PythonTranslator(Translator):
 
         # handle real loop code
         self.indention += 1
-        buf.append(self.indent(self.nodeinfo(node.body)))
+        nodeinfo = self.nodeinfo(node.body)
+        if nodeinfo:
+            write(nodeinfo)
         buf.append(self.handle_node(node.body))
         self.indention -= 1
 
@@ -378,7 +386,9 @@ class PythonTranslator(Translator):
         if node.else_:
             write('if not context[\'loop\'].iterated:')
             self.indention += 1
-            buf.append(self.indent(self.nodeinfo(node.else_)))
+            nodeinfo = self.nodeinfo(node.else_)
+            if nodeinfo:
+                write(nodeinfo)
             buf.append(self.handle_node(node.else_))
             self.indention -= 1
 
@@ -404,20 +414,26 @@ class PythonTranslator(Translator):
         """
         buf = []
         write = lambda x: buf.append(self.indent(x))
-        write(self.nodeinfo(node))
+        nodeinfo = self.nodeinfo(node)
+        if nodeinfo:
+            write(nodeinfo)
         for idx, (test, body) in enumerate(node.tests):
             write('%sif %s:' % (
                 idx and 'el' or '',
                 self.handle_node(test)
             ))
             self.indention += 1
-            write(self.nodeinfo(node))
+            nodeinfo = self.nodeinfo(body)
+            if nodeinfo:
+                write(nodeinfo)
             buf.append(self.handle_node(body))
             self.indention -= 1
         if node.else_ is not None:
             write('else:')
             self.indention += 1
-            write(self.nodeinfo(node))
+            nodeinfo = self.nodeinfo(node.else_)
+            if nodeinfo:
+                write(nodeinfo)
             buf.append(self.handle_node(node.else_))
             self.indention -= 1
         return '\n'.join(buf)
@@ -433,7 +449,9 @@ class PythonTranslator(Translator):
 
         write('if not %r in context.current:' % name)
         self.indention += 1
-        write(self.nodeinfo(node))
+        nodeinfo = self.nodeinfo(node)
+        if nodeinfo:
+            write(nodeinfo)
         if node.seq.__class__ in (ast.Tuple, ast.List):
             write('context.current[%r] = CycleContext(%s)' % (
                 name,
@@ -459,9 +477,11 @@ class PythonTranslator(Translator):
         """
         Handle a print statement.
         """
-        return self.indent(self.nodeinfo(node)) + '\n' + \
-               self.indent('yield finish_var(%s)' %
-                           self.handle_node(node.variable))
+        nodeinfo = self.nodeinfo(node) or ''
+        if nodeinfo:
+            nodeinfo = self.indent(nodeinfo) + '\n'
+        return nodeinfo + self.indent('yield finish_var(%s)' %
+                                      self.handle_node(node.variable))
 
     def handle_macro(self, node):
         """
@@ -472,7 +492,9 @@ class PythonTranslator(Translator):
 
         write('def macro(*args):')
         self.indention += 1
-        write(self.nodeinfo(node))
+        nodeinfo = self.nodeinfo(node)
+        if nodeinfo:
+            write(nodeinfo)
 
         if node.arguments:
             write('argcount = len(args)')
@@ -488,7 +510,9 @@ class PythonTranslator(Translator):
         else:
             write('ctx_push()')
 
-        write(self.nodeinfo(node.body))
+        nodeinfo = self.nodeinfo(node.body)
+        if nodeinfo:
+            write(nodeinfo)
         buf.append(self.handle_node(node.body))
         write('ctx_pop()')
         write('if False:')
@@ -503,8 +527,10 @@ class PythonTranslator(Translator):
         """
         Handle variable assignments.
         """
-        return self.indent(self.nodeinfo(node)) + '\n' + \
-               self.indent('context[%r] = %s' % (
+        nodeinfo = self.nodeinfo(node) or ''
+        if nodeinfo:
+            nodeinfo = self.indent(nodeinfo) + '\n'
+        return nodeinfo + self.indent('context[%r] = %s' % (
             node.name,
             self.handle_node(node.expr)
         ))
@@ -517,9 +543,10 @@ class PythonTranslator(Translator):
         write = lambda x: buf.append(self.indent(x))
         write('def filtered():')
         self.indention += 1
-        write(self.nodeinfo(node))
         write('ctx_push()')
-        write(self.nodeinfo(node.body))
+        nodeinfo = self.nodeinfo(node.body)
+        if nodeinfo:
+            write(nodeinfo)
         buf.append(self.handle_node(node.body))
         write('ctx_pop()')
         write('if False:')
@@ -543,7 +570,9 @@ class PythonTranslator(Translator):
         write = lambda x: buf.append(self.indent(x))
 
         write('ctx_push()')
-        write(self.nodeinfo(node.body))
+        nodeinfo = self.nodeinfo(node.body)
+        if nodeinfo:
+            write(nodebody)
         buf.append(self.handle_node(node.body))
         write('ctx_pop()')
         return '\n'.join(buf)
@@ -571,8 +600,10 @@ class PythonTranslator(Translator):
             replacements = '{%s}' % ', '.join(replacements)
         else:
             replacements = 'None'
-        return self.indent(self.nodeinfo(node)) + '\n' + \
-               self.indent('yield translate(%r, %r, %r, %s)' % (
+        nodeinfo = self.nodeinfo(node) or ''
+        if nodeinfo:
+            nodeinfo = self.indent(nodeinfo) + '\n'
+        return nodeinfo + self.indent('yield translate(%r, %r, %r, %s)' % (
             node.singular,
             node.plural,
             node.indicator,
index d0f57065bc72c6c96f5c1e4467d3744d2d1b3b04..21e5eadb1f3954e09d4d7fb0d4ad99c72650ed3b 100644 (file)
@@ -174,7 +174,7 @@ def buffereater(f):
     return wrapped
 
 
-def raise_template_exception(template, exception, filename, lineno, context):
+def raise_template_exception(exception, filename, lineno, context):
     """
     Raise an exception "in a template". Return a traceback
     object.
@@ -188,7 +188,8 @@ def raise_template_exception(template, exception, filename, lineno, context):
     globals = {
         '__name__':                 filename,
         '__file__':                 filename,
-        '__loader__':               TracebackLoader(template),
+        '__loader__':               TracebackLoader(context.environment,
+                                                    filename),
         '__exception_to_raise__':   exception
     }
     try:
@@ -204,7 +205,7 @@ def translate_exception(template, exc_type, exc_value, traceback, context):
     """
     sourcelines = template.translated_source.splitlines()
     startpos = traceback.tb_lineno - 1
-    args = None
+    filename = None
     # looks like we loaded the template from string. we cannot
     # do anything here.
     if startpos > len(sourcelines):
@@ -213,15 +214,21 @@ def translate_exception(template, exc_type, exc_value, traceback, context):
     while startpos > 0:
         m = _debug_info_re.search(sourcelines[startpos])
         if m is not None:
-            args = m.groups()
+            filename, lineno = m.groups()
+            if filename == 'None':
+                filename = ''
+            if lineno == 'None':
+                lineno = 0
+            else:
+                lineno = int(lineno)
             break
         startpos -= 1
 
     # no traceback information found, reraise unchanged
-    if args is None:
+    if filename is None:
         return traceback
-    return raise_template_exception(template, exc_value, args[0],
-                                    int(args[1] or 0), context)
+    return raise_template_exception(exc_value, filename,
+                                    lineno, context)
 
 
 class TracebackLoader(object):
@@ -229,11 +236,12 @@ class TracebackLoader(object):
     Fake importer that just returns the source of a template.
     """
 
-    def __init__(self, template):
-        self.template = template
+    def __init__(self, environment, filename):
+        self.loader = environment.loader
+        self.filename = filename
 
     def get_source(self, impname):
-        return self.template.source
+        return self.loader.get_source(self.filename)
 
 
 class CacheDict(object):
index fdcc7e13151918255a6c0eb4b243f6bfdcd70fa9..61a298d3711b349405015d7c282264eb6e57afcf 100644 (file)
@@ -7,4 +7,4 @@ def test(*args):
     tmpl = e.loader.load('error.html')
     tmpl.render(items=range(10))
 
-make_server("localhost", 7000, DebuggedApplication(test, False)).serve_forever()
+make_server("localhost", 7000, DebuggedApplication(test)).serve_forever()
index ef11dfb556b33ace27324466837d1387711cccfe..da3f60d4d09f9e030c0c3967165a49d0b7614e7b 100644 (file)
@@ -1,6 +1,6 @@
 <ul>
 <meta http-equiv="Content-Type" content="" />
 {% for item in items %}
-  {{ item + "this will break the application" }}
+  {% include "included_error.html" %}
 {% endfor %}
 </ul>
diff --git a/tests/templates/included_error.html b/tests/templates/included_error.html
new file mode 100644 (file)
index 0000000..da11ba1
--- /dev/null
@@ -0,0 +1,2 @@
+This breaks the template:
+{{ 1 / 0 }}
index 1f1c6d6cb1259cc7294967de85233aa25cd996c5..477030d3f6d2d6b6c4ba53e47f1fd43949289e9e 100644 (file)
@@ -1,4 +1,4 @@
-.syntax { background: #ffffff; }
+.syntax  { background: #ffffff; }
 .syntax .c { color: #888888 } /* Comment */
 .syntax .err { color: #a61717; background-color: #e3d2d2 } /* Error */
 .syntax .k { color: #008800; font-weight: bold } /* Keyword */