[svn] improved debugging support. it's not possible to catch errors of templates...
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 30 Mar 2007 22:02:32 +0000 (00:02 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 30 Mar 2007 22:02:32 +0000 (00:02 +0200)
--HG--
branch : trunk

jinja/environment.py
jinja/translators/python.py
jinja/utils.py
tests/runtime/exception.py

index 92ed4caecbdf78d1126c1f8a14ec36a3cc1465b2..abbaa4b735451d281dff7a1e29153c2b8adf4b6b 100644 (file)
@@ -14,7 +14,8 @@ from jinja.parser import Parser
 from jinja.loaders import LoaderWrapper
 from jinja.datastructure import Undefined, Markup, Context, FakeTranslator
 from jinja.utils import escape, collect_translations, get_attribute
-from jinja.exceptions import FilterNotFound, TestNotFound, SecurityException
+from jinja.exceptions import FilterNotFound, TestNotFound, \
+     SecurityException, TemplateSyntaxError
 from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
 
 
@@ -87,10 +88,18 @@ class Environment(object):
         """Load a template from a string."""
         from jinja.parser import Parser
         from jinja.translators.python import PythonTranslator
-        rv = PythonTranslator.process(self, Parser(self, source).parse())
-        # attach the source for debugging
-        rv._source = source
-        return rv
+        try:
+            rv = PythonTranslator.process(self, Parser(self, source).parse())
+        except TemplateSyntaxError, e:
+            # if errors occour raise a better traceback
+            from jinja.utils import raise_syntax_error
+            __traceback_hide__ = True
+            raise_syntax_error(e, self, source)
+        else:
+            # everything went well. attach the source and return it
+            # attach the source for debugging
+            rv._source = source
+            return rv
 
     def get_template(self, filename):
         """Load a template from a filename. Only works
index 4bc4df1b67671f106bb7cac7fad7ad89a0de3b55..db9f52c5d4d269d3b7c1d43c13fba5bacf9bfa59 100644 (file)
@@ -389,9 +389,8 @@ class PythonTranslator(Translator):
             if m is not None:
                 d = m.groupdict()
                 this = (d['filename'] or None, int(d['lineno']))
-                # if there is no filename in this debug symbol
                 # if it's the same as the line before we ignore it
-                if this[0] and this != last:
+                if this != last:
                     debug_mapping.append((idx - offset,) + this)
                     last = this
                 # for each debug symbol the line number and so the offset
index 4e0183e3b660a41bcab5de8d9259e4ef2b308884..b430312f7bddeb4f52c323b985c7c3200cf97c8f 100644 (file)
@@ -222,7 +222,7 @@ def buffereater(f):
     return wrapped
 
 
-def fake_template_exception(exception, filename, lineno, template,
+def fake_template_exception(exception, filename, lineno, source,
                             context_or_env):
     """
     Raise an exception "in a template". Return a traceback
@@ -238,11 +238,12 @@ def fake_template_exception(exception, filename, lineno, template,
         namespace = {}
 
     offset = '\n' * (lineno - 1)
-    code = compile(offset + 'raise __exception_to_raise__', filename, 'exec')
+    code = compile(offset + 'raise __exception_to_raise__',
+                   filename or '<template>', 'exec')
     globals = {
         '__name__':                 filename,
         '__file__':                 filename,
-        '__loader__':               TracebackLoader(env, template, filename),
+        '__loader__':               TracebackLoader(env, source, filename),
         '__exception_to_raise__':   exception
     }
     try:
@@ -264,10 +265,10 @@ def translate_exception(template, exc_type, exc_value, traceback, context):
         return traceback
 
     return fake_template_exception(exc_value, tmpl_filename, tmpl_line,
-                                   template, context)[2]
+                                   template._source, context)[2]
 
 
-def raise_syntax_error(exception, env):
+def raise_syntax_error(exception, env, source=None):
     """
     This method raises an exception that includes more debugging
     informations so that debugging works better. Unlike
@@ -275,7 +276,7 @@ def raise_syntax_error(exception, env):
     the traceback.
     """
     exc_info = fake_template_exception(exception, exception.filename,
-                                       exception.lineno, None, env)
+                                       exception.lineno, source, env)
     raise exc_info[0], exc_info[1], exc_info[2]
 
 
@@ -315,19 +316,19 @@ class TracebackLoader(object):
     Fake importer that just returns the source of a template.
     """
 
-    def __init__(self, environment, template, filename):
+    def __init__(self, environment, source, filename):
         self.loader = environment.loader
-        self.template = template
+        self.source = source
         self.filename = filename
 
     def get_source(self, impname):
-        if self.loader is not None:
+        if self.source is not None:
+            return self.source
+        elif self.loader is not None:
             try:
                 return self.loader.get_source(self.filename)
             except TemplateNotFound:
                 pass
-        if self.template is not None:
-            return self.template._source or ''
         return ''
 
 
index c27a6df7a091b945aa7169e0b286a9a5825da123..3d2adbdc2a8e5a19bdce762f238df85824ce7f52 100644 (file)
@@ -32,6 +32,8 @@ e = Environment(loader=DictLoader({
       <li><a href="runtime_error">runtime error</a></li>
       <li><a href="nested_syntax_error">nested syntax error</a></li>
       <li><a href="nested_runtime_error">nested runtime error</a></li>
+      <li><a href="syntax_from_string">a syntax error from string</a></li>
+      <li><a href="runtime_from_string">runtime error from a string</a></li>
     </ul>
   </body>
 </html>
@@ -59,13 +61,22 @@ This is an included template
 {% raw %}just some foo'''
 }))
 
+FAILING_STRING_TEMPLATE = '{{ 1 / 0 }}'
+BROKEN_STRING_TEMPLATE = '{% if foo %}...{% endfor %}'
+
 
 def test(environ, start_response):
+    path = environ.get('PATH_INFO' or '/')
     try:
-        tmpl = e.get_template(environ.get('PATH_INFO') or '/')
+        tmpl = e.get_template(path)
     except TemplateNotFound:
-        start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
-        return ['NOT FOUND']
+        if path == '/syntax_from_string':
+            tmpl = e.from_string(BROKEN_STRING_TEMPLATE)
+        elif path == '/runtime_from_string':
+            tmpl = e.from_string(FAILING_STRING_TEMPLATE)
+        else:
+            start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
+            return ['NOT FOUND']
     start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
     return [tmpl.render().encode('utf-8')]