Index: django/views/debug.py
===================================================================
--- django/views/debug.py (revision 16772)
+++ django/views/debug.py (working copy)
@@ -223,8 +223,7 @@
'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 %}
-
Template error
+
Error occured during template rendering
In template {{ template_info.name }}
, error at line {{ template_info.line }}
{{ template_info.message }}
Index: django/template/debug.py
===================================================================
--- django/template/debug.py (revision 16772)
+++ django/template/debug.py (working copy)
@@ -3,7 +3,9 @@
from django.utils.html import escape
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 +44,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.source_template_node = source #Identify the template node that was being rendered when the error occurred.
return e
def create_nodelist(self):
@@ -67,21 +69,17 @@
e.source = token.source
class DebugNodeList(NodeList):
+ '''
+ A list of nodes that is instantiated when debug is True - this is the nerve center of exceptions that occur during template rendering.
+ '''
def render_node(self, node, context):
- try:
- result = node.render(context)
- except TemplateSyntaxError, e:
- if not hasattr(e, 'source'):
- e.source = node.source
- raise
+ try:
+ 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, 'source_template_node'): #Have we already identified the node where the problem occured?
+ e.django_template_source = node.source #...if not, let's annotate the exception with that - the template will use this to show information about the template just before the traceback on the exception page.
+ raise e
+
class DebugVariableNode(VariableNode):
def render(self, context):
Index: tests/regressiontests/templates/tests.py
===================================================================
--- tests/regressiontests/templates/tests.py (revision 16772)
+++ tests/regressiontests/templates/tests.py (working copy)
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.conf import settings
+from django.core.urlresolvers import NoReverseMatch
if __name__ == '__main__':
# When running this file in isolation, we need to set up the configuration
@@ -20,7 +21,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 +310,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 +337,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 +346,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
@@ -370,15 +371,26 @@
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.")
+ self.assertRaises(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):
+ """
+ Previously exceptions were wrapped by TemplateSyntaxError. See #16770
+ """
+ from django.template import Template, TemplateSyntaxError
+ c = Context({'coconuts': lambda: 42 / 0})
+ t = Template("{{coconuts}}")
+ self.assertRaises(ZeroDivisionError, t.render, c) #Dividing by zero ought to cause ZeroDivisionError; we'll instead get TemplateSyntaxError here iff it's been wrapped.
+
+ #Let's also confirm that the exception has the attribute we need to show the template information on the exception page.
+ try:
+ t.render(c)
+ except ZeroDivisionError, e:
+ self.assertTrue(hasattr(e, 'django_template_source'))
def test_invalid_block_suggestion(self):
# See #7876
@@ -666,7 +678,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 +707,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 +765,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 +1062,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 +1493,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 +1534,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 +1543,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),
Index: tests/regressiontests/templates/nodelist.py
===================================================================
--- tests/regressiontests/templates/nodelist.py (revision 16772)
+++ tests/regressiontests/templates/nodelist.py (working copy)
@@ -2,6 +2,7 @@
from django.template import VariableNode, Context, TemplateSyntaxError
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,19 +36,16 @@
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)),
- ('{% load bad_tag %}{% for i in range %}{% for j in range %}{% badsimpletag %}{% endfor %}{% endfor %}', (58, 76)),
- ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% for j in range %}Hello{% endfor %}{% endfor %}', (38, 56)),
- ('{% load bad_tag %}{% for i in range %}{% for j in five %}{% badsimpletag %}{% endfor %}{% endfor %}', (38, 57)),
+ '''
+ Looks at an exception page and confirms that the information about the source of an error that occurs during template rendering appears in the appropriate location.
+ '''
+ tests = [ #In each case, we have template contents and the lines at which we expect the template error information to occur.
+ ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% endfor %}', (18, 38)),
+ ('{% load bad_tag %}{% for i in range %}{% for j in range %}{% badsimpletag %}{% endfor %}{% endfor %}', (18, 38)),
+ ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% for j in range %}Hello{% endfor %}{% endfor %}', (18, 38)),
+ ('{% load bad_tag %}{% for i in range %}{% for j in five %}{% badsimpletag %}{% endfor %}{% endfor %}', (18, 38)),
('{% load bad_tag %}{% for j in five %}{% badsimpletag %}{% endfor %}', (18, 37)),
]
context = Context({
@@ -58,7 +56,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)