Author: carljm
Date: 2011-09-15 18:16:25 -0700 (Thu, 15 Sep 2011)
New Revision: 16833

Modified:
   django/trunk/django/template/debug.py
   django/trunk/django/template/defaulttags.py
   django/trunk/django/views/debug.py
   django/trunk/tests/regressiontests/templates/nodelist.py
   django/trunk/tests/regressiontests/templates/tests.py
   django/trunk/tests/regressiontests/views/tests/debug.py
Log:
Fixed #16770 -- Eliminated TemplateSyntaxError wrapping of exceptions. Thanks 
to Justin Myles-Holmes for report and draft patch.

Exceptions raised in templates were previously wrapped in TemplateSyntaxError
(in TEMPLATE_DEBUG mode only) in order to provide template source details on
the debug 500 page. The same debug information is now provided by annotating
exceptions rather than wrapping them. This makes catching exceptions raised
from templates more sane, as it's consistent in or out of DEBUG, and you can
catch the specific exception(s) you care about rather than having to also catch
TemplateSyntaxError and unwrap it.

Modified: django/trunk/django/template/debug.py
===================================================================
--- django/trunk/django/template/debug.py       2011-09-15 23:55:30 UTC (rev 
16832)
+++ django/trunk/django/template/debug.py       2011-09-16 01:16:25 UTC (rev 
16833)
@@ -4,6 +4,7 @@
 from django.utils.safestring import SafeData, EscapeData
 from django.utils.formats import localize
 
+
 class DebugLexer(Lexer):
     def __init__(self, template_string, origin):
         super(DebugLexer, self).__init__(template_string, origin)
@@ -42,9 +43,9 @@
     def error(self, token, msg):
         return self.source_error(token.source, msg)
 
-    def source_error(self, source,msg):
+    def source_error(self, source, msg):
         e = TemplateSyntaxError(msg)
-        e.source = source
+        e.django_template_source = source
         return e
 
     def create_nodelist(self):
@@ -63,38 +64,31 @@
         raise self.source_error(source, msg)
 
     def compile_function_error(self, token, e):
-        if not hasattr(e, 'source'):
-            e.source = token.source
+        if not hasattr(e, 'django_template_source'):
+            e.django_template_source = token.source
 
 class DebugNodeList(NodeList):
     def render_node(self, node, context):
         try:
-            result = node.render(context)
-        except TemplateSyntaxError, e:
-            if not hasattr(e, 'source'):
-                e.source = node.source
-            raise
+            return node.render(context)
         except Exception, e:
-            from sys import exc_info
-            wrapped = TemplateSyntaxError(u'Caught %s while rendering: %s' %
-                (e.__class__.__name__, force_unicode(e, errors='replace')))
-            wrapped.source = getattr(e, 'template_node_source', node.source)
-            wrapped.exc_info = exc_info()
-            raise wrapped, None, wrapped.exc_info[2]
-        return result
+            if not hasattr(e, 'django_template_source'):
+                e.django_template_source = node.source
+            raise
 
+
 class DebugVariableNode(VariableNode):
     def render(self, context):
         try:
             output = self.filter_expression.resolve(context)
             output = localize(output, use_l10n=context.use_l10n)
             output = force_unicode(output)
-        except TemplateSyntaxError, e:
-            if not hasattr(e, 'source'):
-                e.source = self.source
-            raise
         except UnicodeDecodeError:
             return ''
+        except Exception, e:
+            if not hasattr(e, 'django_template_source'):
+                e.django_template_source = self.source
+            raise
         if (context.autoescape and not isinstance(output, SafeData)) or 
isinstance(output, EscapeData):
             return escape(output)
         else:

Modified: django/trunk/django/template/defaulttags.py
===================================================================
--- django/trunk/django/template/defaulttags.py 2011-09-15 23:55:30 UTC (rev 
16832)
+++ django/trunk/django/template/defaulttags.py 2011-09-16 01:16:25 UTC (rev 
16833)
@@ -227,17 +227,15 @@
                     context.update(unpacked_vars)
             else:
                 context[self.loopvars[0]] = item
-            # In TEMPLATE_DEBUG mode providing source of the node which
-            # actually raised an exception to DefaultNodeList.render_node
+            # In TEMPLATE_DEBUG mode provide source of the node which
+            # actually raised the exception
             if settings.TEMPLATE_DEBUG:
                 for node in self.nodelist_loop:
                     try:
                         nodelist.append(node.render(context))
                     except Exception, e:
-                        if not hasattr(e, 'template_node_source'):
-                            from sys import exc_info
-                            e.template_node_source = node.source
-                            raise e, None, exc_info()[2]
+                        if not hasattr(e, 'django_template_source'):
+                            e.django_template_source = node.source
                         raise
             else:
                 for node in self.nodelist_loop:

Modified: django/trunk/django/views/debug.py
===================================================================
--- django/trunk/django/views/debug.py  2011-09-15 23:55:30 UTC (rev 16832)
+++ django/trunk/django/views/debug.py  2011-09-16 01:16:25 UTC (rev 16833)
@@ -8,8 +8,7 @@
 from django.core.exceptions import ImproperlyConfigured
 from django.http import (HttpResponse, HttpResponseServerError,
     HttpResponseNotFound, HttpRequest, build_request_repr)
-from django.template import (Template, Context, TemplateDoesNotExist,
-    TemplateSyntaxError)
+from django.template import Template, Context, TemplateDoesNotExist
 from django.template.defaultfilters import force_escape, pprint
 from django.utils.html import escape
 from django.utils.importlib import import_module
@@ -223,8 +222,8 @@
                     'loader': loader_name,
                     'templates': template_list,
                 })
-        if (settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'source') and
-            isinstance(self.exc_value, TemplateSyntaxError)):
+        if (settings.TEMPLATE_DEBUG and
+            hasattr(self.exc_value, 'django_template_source')):
             self.get_template_exception_info()
 
         frames = self.get_traceback_frames()
@@ -268,7 +267,7 @@
         return t.render(c)
 
     def get_template_exception_info(self):
-        origin, (start, end) = self.exc_value.source
+        origin, (start, end) = self.exc_value.django_template_source
         template_source = origin.reload()
         context_lines = 10
         line = 0
@@ -626,7 +625,7 @@
 {% endif %}
 {% if template_info %}
 <div id="template">
-   <h2>Template error</h2>
+   <h2>Error during template rendering</h2>
    <p>In template <code>{{ template_info.name }}</code>, error at line 
<strong>{{ template_info.line }}</strong></p>
    <h3>{{ template_info.message }}</h3>
    <table class="source{% if template_info.top %} cut-top{% endif %}{% 
ifnotequal template_info.bottom template_info.total %} cut-bottom{% 
endifnotequal %}">

Modified: django/trunk/tests/regressiontests/templates/nodelist.py
===================================================================
--- django/trunk/tests/regressiontests/templates/nodelist.py    2011-09-15 
23:55:30 UTC (rev 16832)
+++ django/trunk/tests/regressiontests/templates/nodelist.py    2011-09-16 
01:16:25 UTC (rev 16833)
@@ -1,7 +1,7 @@
-from django.conf import settings
-from django.template import VariableNode, Context, TemplateSyntaxError
+from django.template import VariableNode, Context
 from django.template.loader import get_template_from_string
 from django.utils.unittest import TestCase
+from django.test.utils import override_settings
 
 class NodelistTest(TestCase):
 
@@ -35,13 +35,7 @@
     Checks whether index of error is calculated correctly in
     template debugger in for loops. Refs ticket #5831
     """
-    def setUp(self):
-        self.old_template_debug = settings.TEMPLATE_DEBUG
-        settings.TEMPLATE_DEBUG = True
-
-    def tearDown(self):
-        settings.TEMPLATE_DEBUG = self.old_template_debug
-
+    @override_settings(DEBUG=True, TEMPLATE_DEBUG = True)
     def test_correct_exception_index(self):
         tests = [
             ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% 
endfor %}', (38, 56)),
@@ -58,7 +52,7 @@
             template = get_template_from_string(source)
             try:
                 template.render(context)
-            except TemplateSyntaxError, e:
-                error_source_index = e.source[1]
+            except (RuntimeError, TypeError), e:
+                error_source_index = e.django_template_source[1]
                 self.assertEqual(error_source_index,
                                  expected_error_source_index)

Modified: django/trunk/tests/regressiontests/templates/tests.py
===================================================================
--- django/trunk/tests/regressiontests/templates/tests.py       2011-09-15 
23:55:30 UTC (rev 16832)
+++ django/trunk/tests/regressiontests/templates/tests.py       2011-09-16 
01:16:25 UTC (rev 16833)
@@ -1,4 +1,6 @@
 # -*- coding: utf-8 -*-
+from __future__ import with_statement
+
 from django.conf import settings
 
 if __name__ == '__main__':
@@ -20,7 +22,7 @@
 from django.template import loader
 from django.template.loaders import app_directories, filesystem, cached
 from django.test.utils import (get_warnings_state, restore_warnings_state,
-    setup_test_template_loader, restore_template_loaders)
+    setup_test_template_loader, restore_template_loaders, override_settings)
 from django.utils import unittest
 from django.utils.formats import date_format
 from django.utils.translation import activate, deactivate, ugettext as _
@@ -309,9 +311,9 @@
             r = None
             try:
                 r = tmpl.render(template.Context({}))
-            except template.TemplateSyntaxError, e:
+            except template.TemplateDoesNotExist, e:
                 settings.TEMPLATE_DEBUG = old_td
-                self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while 
rendering: missing.html')
+                self.assertEqual(e.args[0], 'missing.html')
             self.assertEqual(r, None, 'Template rendering unexpectedly 
succeeded, produced: ->%r<-' % r)
         finally:
             loader.template_source_loaders = old_loaders
@@ -336,8 +338,8 @@
             r = None
             try:
                 r = tmpl.render(template.Context({}))
-            except template.TemplateSyntaxError, e:
-                self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while 
rendering: missing.html')
+            except template.TemplateDoesNotExist, e:
+                self.assertEqual(e.args[0], 'missing.html')
             self.assertEqual(r, None, 'Template rendering unexpectedly 
succeeded, produced: ->%r<-' % r)
 
             # For the cached loader, repeat the test, to ensure the first 
attempt did not cache a
@@ -345,8 +347,8 @@
             tmpl = loader.get_template(load_name)
             try:
                 tmpl.render(template.Context({}))
-            except template.TemplateSyntaxError, e:
-                self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while 
rendering: missing.html')
+            except template.TemplateDoesNotExist, e:
+                self.assertEqual(e.args[0], 'missing.html')
             self.assertEqual(r, None, 'Template rendering unexpectedly 
succeeded, produced: ->%r<-' % r)
         finally:
             loader.template_source_loaders = old_loaders
@@ -358,28 +360,32 @@
         split = token.split_contents()
         self.assertEqual(split, ["sometag", '_("Page not found")', 
'value|yesno:_("yes,no")'])
 
+    @override_settings(SETTINGS_MODULE=None, TEMPLATE_DEBUG=True)
     def test_url_reverse_no_settings_module(self):
         # Regression test for #9005
-        from django.template import Template, Context, TemplateSyntaxError
+        from django.template import Template, Context
 
-        old_settings_module = settings.SETTINGS_MODULE
-        old_template_debug = settings.TEMPLATE_DEBUG
-
-        settings.SETTINGS_MODULE = None
-        settings.TEMPLATE_DEBUG = True
-
         t = Template('{% url will_not_match %}')
         c = Context()
-        try:
-            rendered = t.render(c)
-        except TemplateSyntaxError, e:
-            # Assert that we are getting the template syntax error and not the
-            # string encoding error.
-            self.assertEqual(e.args[0], "Caught NoReverseMatch while 
rendering: Reverse for 'will_not_match' with arguments '()' and keyword 
arguments '{}' not found.")
+        with self.assertRaises(urlresolvers.NoReverseMatch):
+            t.render(c)
 
-        settings.SETTINGS_MODULE = old_settings_module
-        settings.TEMPLATE_DEBUG = old_template_debug
 
+    @override_settings(DEBUG=True, TEMPLATE_DEBUG = True)
+    def test_no_wrapped_exception(self):
+        """
+        The template system doesn't wrap exceptions, but annotates them.
+        Refs #16770
+
+        """
+        c = Context({"coconuts": lambda: 42 / 0})
+        t = Template("{{ coconuts }}")
+        with self.assertRaises(ZeroDivisionError) as cm:
+            t.render(c)
+
+        self.assertEqual(cm.exception.django_template_source[1], (0, 14))
+
+
     def test_invalid_block_suggestion(self):
         # See #7876
         from django.template import Template, TemplateSyntaxError
@@ -666,7 +672,7 @@
 
             # In methods that raise an exception without a
             # "silent_variable_attribute" set to True, the exception propagates
-            'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, 
(SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
+            'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, 
(SomeOtherException, SomeOtherException)),
 
             # Escaped backslash in argument
             'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', 
{"var": None}, r'foo\bar'),
@@ -695,8 +701,8 @@
             # In attribute and dict lookups that raise an unexpected exception
             # without a "silent_variable_attribute" set to True, the exception
             # propagates
-            'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": 
SomeClass()}, (SomeOtherException, SomeOtherException, 
template.TemplateSyntaxError)),
-            'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": 
SomeClass()}, (SomeOtherException, SomeOtherException, 
template.TemplateSyntaxError)),
+            'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": 
SomeClass()}, (SomeOtherException, SomeOtherException)),
+            'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": 
SomeClass()}, (SomeOtherException, SomeOtherException)),
 
             ### COMMENT SYNTAX 
########################################################
             'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
@@ -753,7 +759,7 @@
             ### EXCEPTIONS 
############################################################
 
             # Raise exception for invalid template name
-            'exception01': ("{% extends 'nonexistent' %}", {}, 
(template.TemplateDoesNotExist, template.TemplateDoesNotExist, 
template.TemplateSyntaxError)),
+            'exception01': ("{% extends 'nonexistent' %}", {}, 
(template.TemplateDoesNotExist, template.TemplateDoesNotExist)),
 
             # Raise exception for invalid template name (in variable)
             'exception02': ("{% extends nonexistent %}", {}, 
(template.TemplateSyntaxError, template.TemplateDoesNotExist)),
@@ -1050,7 +1056,7 @@
             'include-fail2': ('{% load broken_tag %}', {}, 
template.TemplateSyntaxError),
             'include-error07': ('{% include "include-fail1" %}', {}, ('', '', 
RuntimeError)),
             'include-error08': ('{% include "include-fail2" %}', {}, ('', '', 
template.TemplateSyntaxError)),
-            'include-error09': ('{% include failed_include %}', 
{'failed_include': 'include-fail1'}, ('', '', template.TemplateSyntaxError)),
+            'include-error09': ('{% include failed_include %}', 
{'failed_include': 'include-fail1'}, ('', '', RuntimeError)),
             'include-error10': ('{% include failed_include %}', 
{'failed_include': 'include-fail2'}, ('', '', template.TemplateSyntaxError)),
 
 
@@ -1481,8 +1487,8 @@
 
             # Failures
             'old-url-fail01': ('{% url %}', {}, template.TemplateSyntaxError),
-            'old-url-fail02': ('{% url no_such_view %}', {}, 
(urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, 
template.TemplateSyntaxError)),
-            'old-url-fail03': ('{% url regressiontests.templates.views.client 
%}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, 
template.TemplateSyntaxError)),
+            'old-url-fail02': ('{% url no_such_view %}', {}, 
(urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
+            'old-url-fail03': ('{% url regressiontests.templates.views.client 
%}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
             'old-url-fail04': ('{% url view id, %}', {}, 
template.TemplateSyntaxError),
             'old-url-fail05': ('{% url view id= %}', {}, 
template.TemplateSyntaxError),
             'old-url-fail06': ('{% url view a.id=id %}', {}, 
template.TemplateSyntaxError),
@@ -1522,8 +1528,8 @@
 
             # Failures
             'url-fail01': ('{% load url from future %}{% url %}', {}, 
template.TemplateSyntaxError),
-            'url-fail02': ('{% load url from future %}{% url "no_such_view" 
%}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, 
template.TemplateSyntaxError)),
-            'url-fail03': ('{% load url from future %}{% url 
"regressiontests.templates.views.client" %}', {}, (urlresolvers.NoReverseMatch, 
urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
+            'url-fail02': ('{% load url from future %}{% url "no_such_view" 
%}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
+            'url-fail03': ('{% load url from future %}{% url 
"regressiontests.templates.views.client" %}', {}, (urlresolvers.NoReverseMatch, 
urlresolvers.NoReverseMatch)),
             'url-fail04': ('{% load url from future %}{% url "view" id, %}', 
{}, template.TemplateSyntaxError),
             'url-fail05': ('{% load url from future %}{% url "view" id= %}', 
{}, template.TemplateSyntaxError),
             'url-fail06': ('{% load url from future %}{% url "view" a.id=id 
%}', {}, template.TemplateSyntaxError),
@@ -1531,9 +1537,9 @@
             'url-fail08': ('{% load url from future %}{% url "view" 
id="unterminatedstring %}', {}, template.TemplateSyntaxError),
             'url-fail09': ('{% load url from future %}{% url "view" id=", %}', 
{}, template.TemplateSyntaxError),
 
-            'url-fail11': ('{% load url from future %}{% url named_url %}', 
{}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, 
template.TemplateSyntaxError)),
-            'url-fail12': ('{% load url from future %}{% url named_url %}', 
{'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, 
urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
-            'url-fail13': ('{% load url from future %}{% url named_url %}', 
{'named_url': 'regressiontests.templates.views.client'}, 
(urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, 
template.TemplateSyntaxError)),
+            'url-fail11': ('{% load url from future %}{% url named_url %}', 
{}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
+            'url-fail12': ('{% load url from future %}{% url named_url %}', 
{'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, 
urlresolvers.NoReverseMatch)),
+            'url-fail13': ('{% load url from future %}{% url named_url %}', 
{'named_url': 'regressiontests.templates.views.client'}, 
(urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
             'url-fail14': ('{% load url from future %}{% url named_url id, 
%}', {'named_url': 'view'}, template.TemplateSyntaxError),
             'url-fail15': ('{% load url from future %}{% url named_url id= 
%}', {'named_url': 'view'}, template.TemplateSyntaxError),
             'url-fail16': ('{% load url from future %}{% url named_url a.id=id 
%}', {'named_url': 'view'}, template.TemplateSyntaxError),

Modified: django/trunk/tests/regressiontests/views/tests/debug.py
===================================================================
--- django/trunk/tests/regressiontests/views/tests/debug.py     2011-09-15 
23:55:30 UTC (rev 16832)
+++ django/trunk/tests/regressiontests/views/tests/debug.py     2011-09-16 
01:16:25 UTC (rev 16833)
@@ -75,7 +75,7 @@
         for n in range(len(except_args)):
             try:
                 self.client.get(reverse('template_exception', args=(n,)))
-            except TemplateSyntaxError, e:
+            except Exception:
                 raising_loc = inspect.trace()[-1][-2][0].strip()
                 self.assertFalse(raising_loc.find('raise BrokenException') == 
-1,
                     "Failed to find 'raise BrokenException' in last frame of 
traceback, instead found: %s" %

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to