improved debugging for syntax errors
authorArmin Ronacher <armin.ronacher@active-4.com>
Thu, 1 May 2008 11:14:30 +0000 (13:14 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Thu, 1 May 2008 11:14:30 +0000 (13:14 +0200)
--HG--
branch : trunk

jinja2/datastructure.py
jinja2/debug.py
jinja2/environment.py
jinja2/exceptions.py

index 9dab02b83099d4abe710ceb695359f9c09e7b673..51ffc93a29a2ae1d2df5814dce1927ec3f52c104 100644 (file)
@@ -29,7 +29,9 @@ class Token(tuple):
             return self.type
         elif self.type in reverse_operators:
             return reverse_operators[self.type]
-        return '%s:%s' % (self.type, self.value)
+        elif self.type is 'name':
+            return self.value
+        return self.type
 
     def test(self, expr):
         """Test a token against a token expression.  This can either be a
@@ -147,8 +149,15 @@ class TokenStream(object):
     def expect(self, expr):
         """Expect a given token type and return it"""
         if not self.current.test(expr):
+            if ':' in expr:
+                expr = expr.split(':')[1]
+            if self.current.type is 'eof':
+                raise TemplateSyntaxError('unexpected end of template, '
+                                          'expected %r.' % expr,
+                                          self.current.lineno,
+                                          self.filename)
             raise TemplateSyntaxError("expected token %r, got %r" %
-                                      (expr, self.current),
+                                      (expr, str(self.current)),
                                       self.current.lineno,
                                       self.filename)
         try:
index 622f2b3e1a61f10cfe122b4d1dd07781aa54227c..a9b4439f31b38b04ce90eb4b1d2702e262571b3b 100644 (file)
@@ -33,6 +33,24 @@ def translate_exception(exc_info):
     return exc_info[:2] + (result_tb or initial_tb,)
 
 
+def translate_syntax_error(error):
+    """When passed a syntax error it will generate a new traceback with
+    more debugging information.
+    """
+    filename = error.filename
+    if filename is None:
+        filename = '<template>'
+    elif isinstance(filename, unicode):
+        filename = filename.encode('utf-8')
+    code = compile('\n' * (error.lineno - 1) + 'raise __jinja_exception__',
+                   filename, 'exec')
+    try:
+        exec code in {'__jinja_exception__': error}
+    except:
+        exc_info = sys.exc_info()
+        return exc_info[:2] + (exc_info[2].tb_next,)
+
+
 def fake_exc_info(exc_info, filename, lineno, tb_back=None):
     """Helper for `translate_exception`."""
     exc_type, exc_value, tb = exc_info
index 8b959e5f6fc13a65a537fbb70dbbaaac87d00e18..2c370c6f0f9def941adc35a3e4d441e2d1a1212f 100644 (file)
@@ -15,7 +15,8 @@ from jinja2.parser import Parser
 from jinja2.optimizer import optimize
 from jinja2.compiler import generate
 from jinja2.runtime import Undefined, Context, concat
-from jinja2.debug import translate_exception
+from jinja2.debug import translate_exception, translate_syntax_error
+from jinja2.exceptions import TemplateSyntaxError
 from jinja2.utils import import_string, LRUCache, Markup, missing
 
 
@@ -271,13 +272,17 @@ class Environment(object):
             except (TypeError, LookupError):
                 return self.undefined(obj=obj, name=argument)
 
-    def parse(self, source, name=None):
+    def parse(self, source, filename=None):
         """Parse the sourcecode and return the abstract syntax tree.  This
         tree of nodes is used by the compiler to convert the template into
         executable source- or bytecode.  This is useful for debugging or to
         extract information from templates.
         """
-        return Parser(self, source, name).parse()
+        try:
+            return Parser(self, source, filename).parse()
+        except TemplateSyntaxError, e:
+            exc_type, exc_value, tb = translate_syntax_error(e)
+            raise exc_type, exc_value, tb
 
     def lex(self, source, name=None):
         """Lex the given sourcecode and return a generator that yields
@@ -303,7 +308,7 @@ class Environment(object):
         mainly used internally.
         """
         if isinstance(source, basestring):
-            source = self.parse(source, name)
+            source = self.parse(source, filename)
         if self.optimized:
             node = optimize(source, self, globals or {})
         source = generate(node, self, name, filename)
index b0853c4f60284e496ae9ee2436d25319282088be..e5b156b9f90fafa646ac54c1398c7f9b92cdfce4 100644 (file)
@@ -29,11 +29,11 @@ class TemplateNotFound(IOError, LookupError, TemplateError):
 class TemplateSyntaxError(TemplateError):
     """Raised to tell the user that there is a problem with the template."""
 
-    def __init__(self, message, lineno, name):
+    def __init__(self, message, lineno, filename):
         TemplateError.__init__(self, '%s (line %s)' % (message, lineno))
         self.message = message
         self.lineno = lineno
-        self.name = name
+        self.filename = filename
 
 
 class TemplateAssertionError(TemplateSyntaxError):