1 | Index: django/views/debug.py
|
---|
2 | ===================================================================
|
---|
3 | --- django/views/debug.py (revision 16772)
|
---|
4 | +++ django/views/debug.py (working copy)
|
---|
5 | @@ -223,8 +223,7 @@
|
---|
6 | 'loader': loader_name,
|
---|
7 | 'templates': template_list,
|
---|
8 | })
|
---|
9 | - if (settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'source') and
|
---|
10 | - isinstance(self.exc_value, TemplateSyntaxError)):
|
---|
11 | + if (settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'django_template_source')):
|
---|
12 | self.get_template_exception_info()
|
---|
13 |
|
---|
14 | frames = self.get_traceback_frames()
|
---|
15 | @@ -268,7 +267,7 @@
|
---|
16 | return t.render(c)
|
---|
17 |
|
---|
18 | def get_template_exception_info(self):
|
---|
19 | - origin, (start, end) = self.exc_value.source
|
---|
20 | + origin, (start, end) = self.exc_value.django_template_source
|
---|
21 | template_source = origin.reload()
|
---|
22 | context_lines = 10
|
---|
23 | line = 0
|
---|
24 | @@ -626,7 +625,7 @@
|
---|
25 | {% endif %}
|
---|
26 | {% if template_info %}
|
---|
27 | <div id="template">
|
---|
28 | - <h2>Template error</h2>
|
---|
29 | + <h2>Error occured during template rendering</h2>
|
---|
30 | <p>In template <code>{{ template_info.name }}</code>, error at line <strong>{{ template_info.line }}</strong></p>
|
---|
31 | <h3>{{ template_info.message }}</h3>
|
---|
32 | <table class="source{% if template_info.top %} cut-top{% endif %}{% ifnotequal template_info.bottom template_info.total %} cut-bottom{% endifnotequal %}">
|
---|
33 | Index: django/template/debug.py
|
---|
34 | ===================================================================
|
---|
35 | --- django/template/debug.py (revision 16772)
|
---|
36 | +++ django/template/debug.py (working copy)
|
---|
37 | @@ -3,7 +3,9 @@
|
---|
38 | from django.utils.html import escape
|
---|
39 | from django.utils.safestring import SafeData, EscapeData
|
---|
40 | from django.utils.formats import localize
|
---|
41 |
|
---|
42 | class DebugLexer(Lexer):
|
---|
43 | def __init__(self, template_string, origin):
|
---|
44 | super(DebugLexer, self).__init__(template_string, origin)
|
---|
45 | @@ -42,9 +44,9 @@
|
---|
46 | def error(self, token, msg):
|
---|
47 | return self.source_error(token.source, msg)
|
---|
48 |
|
---|
49 | - def source_error(self, source,msg):
|
---|
50 | + def source_error(self, source, msg):
|
---|
51 | e = TemplateSyntaxError(msg)
|
---|
52 | - e.source = source
|
---|
53 | + e.source_template_node = source #Identify the template node that was being rendered when the error occurred.
|
---|
54 | return e
|
---|
55 |
|
---|
56 | def create_nodelist(self):
|
---|
57 | @@ -67,21 +69,17 @@
|
---|
58 | e.source = token.source
|
---|
59 |
|
---|
60 | class DebugNodeList(NodeList):
|
---|
61 | + '''
|
---|
62 | + A list of nodes that is instantiated when debug is True - this is the nerve center of exceptions that occur during template rendering.
|
---|
63 | + '''
|
---|
64 | def render_node(self, node, context):
|
---|
65 | - try:
|
---|
66 | - result = node.render(context)
|
---|
67 | - except TemplateSyntaxError, e:
|
---|
68 | - if not hasattr(e, 'source'):
|
---|
69 | - e.source = node.source
|
---|
70 | - raise
|
---|
71 | + try:
|
---|
72 | + return node.render(context)
|
---|
73 | except Exception, e:
|
---|
74 | - from sys import exc_info
|
---|
75 | - wrapped = TemplateSyntaxError(u'Caught %s while rendering: %s' %
|
---|
76 | - (e.__class__.__name__, force_unicode(e, errors='replace')))
|
---|
77 | - wrapped.source = getattr(e, 'template_node_source', node.source)
|
---|
78 | - wrapped.exc_info = exc_info()
|
---|
79 | - raise wrapped, None, wrapped.exc_info[2]
|
---|
80 | - return result
|
---|
81 | + if not hasattr(e, 'source_template_node'): #Have we already identified the node where the problem occured?
|
---|
82 | + 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.
|
---|
83 | + raise e
|
---|
84 | +
|
---|
85 |
|
---|
86 | class DebugVariableNode(VariableNode):
|
---|
87 | def render(self, context):
|
---|
88 | Index: tests/regressiontests/templates/tests.py
|
---|
89 | ===================================================================
|
---|
90 | --- tests/regressiontests/templates/tests.py (revision 16772)
|
---|
91 | +++ tests/regressiontests/templates/tests.py (working copy)
|
---|
92 | @@ -1,5 +1,6 @@
|
---|
93 | # -*- coding: utf-8 -*-
|
---|
94 | from django.conf import settings
|
---|
95 | +from django.core.urlresolvers import NoReverseMatch
|
---|
96 |
|
---|
97 | if __name__ == '__main__':
|
---|
98 | # When running this file in isolation, we need to set up the configuration
|
---|
99 | @@ -20,7 +21,7 @@
|
---|
100 | from django.template import loader
|
---|
101 | from django.template.loaders import app_directories, filesystem, cached
|
---|
102 | from django.test.utils import (get_warnings_state, restore_warnings_state,
|
---|
103 | - setup_test_template_loader, restore_template_loaders)
|
---|
104 | + setup_test_template_loader, restore_template_loaders, override_settings)
|
---|
105 | from django.utils import unittest
|
---|
106 | from django.utils.formats import date_format
|
---|
107 | from django.utils.translation import activate, deactivate, ugettext as _
|
---|
108 | @@ -309,9 +310,9 @@
|
---|
109 | r = None
|
---|
110 | try:
|
---|
111 | r = tmpl.render(template.Context({}))
|
---|
112 | - except template.TemplateSyntaxError, e:
|
---|
113 | + except template.TemplateDoesNotExist, e:
|
---|
114 | settings.TEMPLATE_DEBUG = old_td
|
---|
115 | - self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
|
---|
116 | + self.assertEqual(e.args[0], 'missing.html')
|
---|
117 | self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
|
---|
118 | finally:
|
---|
119 | loader.template_source_loaders = old_loaders
|
---|
120 | @@ -336,8 +337,8 @@
|
---|
121 | r = None
|
---|
122 | try:
|
---|
123 | r = tmpl.render(template.Context({}))
|
---|
124 | - except template.TemplateSyntaxError, e:
|
---|
125 | - self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
|
---|
126 | + except template.TemplateDoesNotExist, e:
|
---|
127 | + self.assertEqual(e.args[0], 'missing.html')
|
---|
128 | self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
|
---|
129 |
|
---|
130 | # For the cached loader, repeat the test, to ensure the first attempt did not cache a
|
---|
131 | @@ -345,8 +346,8 @@
|
---|
132 | tmpl = loader.get_template(load_name)
|
---|
133 | try:
|
---|
134 | tmpl.render(template.Context({}))
|
---|
135 | - except template.TemplateSyntaxError, e:
|
---|
136 | - self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
|
---|
137 | + except template.TemplateDoesNotExist, e:
|
---|
138 | + self.assertEqual(e.args[0], 'missing.html')
|
---|
139 | self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
|
---|
140 | finally:
|
---|
141 | loader.template_source_loaders = old_loaders
|
---|
142 | @@ -370,15 +371,26 @@
|
---|
143 |
|
---|
144 | t = Template('{% url will_not_match %}')
|
---|
145 | c = Context()
|
---|
146 | - try:
|
---|
147 | - rendered = t.render(c)
|
---|
148 | - except TemplateSyntaxError, e:
|
---|
149 | - # Assert that we are getting the template syntax error and not the
|
---|
150 | - # string encoding error.
|
---|
151 | - self.assertEqual(e.args[0], "Caught NoReverseMatch while rendering: Reverse for 'will_not_match' with arguments '()' and keyword arguments '{}' not found.")
|
---|
152 | + self.assertRaises(NoReverseMatch, t.render, c)
|
---|
153 |
|
---|
154 | settings.SETTINGS_MODULE = old_settings_module
|
---|
155 | settings.TEMPLATE_DEBUG = old_template_debug
|
---|
156 | +
|
---|
157 | + @override_settings(DEBUG=True, TEMPLATE_DEBUG = True)
|
---|
158 | + def test_no_wrapped_exception(self):
|
---|
159 | + """
|
---|
160 | + Previously exceptions were wrapped by TemplateSyntaxError. See #16770
|
---|
161 | + """
|
---|
162 | + from django.template import Template, TemplateSyntaxError
|
---|
163 | + c = Context({'coconuts': lambda: 42 / 0})
|
---|
164 | + t = Template("{{coconuts}}")
|
---|
165 | + self.assertRaises(ZeroDivisionError, t.render, c) #Dividing by zero ought to cause ZeroDivisionError; we'll instead get TemplateSyntaxError here iff it's been wrapped.
|
---|
166 | +
|
---|
167 | + #Let's also confirm that the exception has the attribute we need to show the template information on the exception page.
|
---|
168 | + try:
|
---|
169 | + t.render(c)
|
---|
170 | + except ZeroDivisionError, e:
|
---|
171 | + self.assertTrue(hasattr(e, 'django_template_source'))
|
---|
172 |
|
---|
173 | def test_invalid_block_suggestion(self):
|
---|
174 | # See #7876
|
---|
175 | @@ -666,7 +678,7 @@
|
---|
176 |
|
---|
177 | # In methods that raise an exception without a
|
---|
178 | # "silent_variable_attribute" set to True, the exception propagates
|
---|
179 | - 'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
|
---|
180 | + 'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)),
|
---|
181 |
|
---|
182 | # Escaped backslash in argument
|
---|
183 | 'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'),
|
---|
184 | @@ -695,8 +707,8 @@
|
---|
185 | # In attribute and dict lookups that raise an unexpected exception
|
---|
186 | # without a "silent_variable_attribute" set to True, the exception
|
---|
187 | # propagates
|
---|
188 | - 'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
|
---|
189 | - 'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
|
---|
190 | + 'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)),
|
---|
191 | + 'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)),
|
---|
192 |
|
---|
193 | ### COMMENT SYNTAX ########################################################
|
---|
194 | 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
|
---|
195 | @@ -753,7 +765,7 @@
|
---|
196 | ### EXCEPTIONS ############################################################
|
---|
197 |
|
---|
198 | # Raise exception for invalid template name
|
---|
199 | - 'exception01': ("{% extends 'nonexistent' %}", {}, (template.TemplateDoesNotExist, template.TemplateDoesNotExist, template.TemplateSyntaxError)),
|
---|
200 | + 'exception01': ("{% extends 'nonexistent' %}", {}, (template.TemplateDoesNotExist, template.TemplateDoesNotExist)),
|
---|
201 |
|
---|
202 | # Raise exception for invalid template name (in variable)
|
---|
203 | 'exception02': ("{% extends nonexistent %}", {}, (template.TemplateSyntaxError, template.TemplateDoesNotExist)),
|
---|
204 | @@ -1050,7 +1062,7 @@
|
---|
205 | 'include-fail2': ('{% load broken_tag %}', {}, template.TemplateSyntaxError),
|
---|
206 | 'include-error07': ('{% include "include-fail1" %}', {}, ('', '', RuntimeError)),
|
---|
207 | 'include-error08': ('{% include "include-fail2" %}', {}, ('', '', template.TemplateSyntaxError)),
|
---|
208 | - 'include-error09': ('{% include failed_include %}', {'failed_include': 'include-fail1'}, ('', '', template.TemplateSyntaxError)),
|
---|
209 | + 'include-error09': ('{% include failed_include %}', {'failed_include': 'include-fail1'}, ('', '', RuntimeError)),
|
---|
210 | 'include-error10': ('{% include failed_include %}', {'failed_include': 'include-fail2'}, ('', '', template.TemplateSyntaxError)),
|
---|
211 |
|
---|
212 |
|
---|
213 | @@ -1481,8 +1493,8 @@
|
---|
214 |
|
---|
215 | # Failures
|
---|
216 | 'old-url-fail01': ('{% url %}', {}, template.TemplateSyntaxError),
|
---|
217 | - 'old-url-fail02': ('{% url no_such_view %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
218 | - 'old-url-fail03': ('{% url regressiontests.templates.views.client %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
219 | + 'old-url-fail02': ('{% url no_such_view %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
220 | + 'old-url-fail03': ('{% url regressiontests.templates.views.client %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
221 | 'old-url-fail04': ('{% url view id, %}', {}, template.TemplateSyntaxError),
|
---|
222 | 'old-url-fail05': ('{% url view id= %}', {}, template.TemplateSyntaxError),
|
---|
223 | 'old-url-fail06': ('{% url view a.id=id %}', {}, template.TemplateSyntaxError),
|
---|
224 | @@ -1522,8 +1534,8 @@
|
---|
225 |
|
---|
226 | # Failures
|
---|
227 | 'url-fail01': ('{% load url from future %}{% url %}', {}, template.TemplateSyntaxError),
|
---|
228 | - 'url-fail02': ('{% load url from future %}{% url "no_such_view" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
229 | - 'url-fail03': ('{% load url from future %}{% url "regressiontests.templates.views.client" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
230 | + 'url-fail02': ('{% load url from future %}{% url "no_such_view" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
231 | + 'url-fail03': ('{% load url from future %}{% url "regressiontests.templates.views.client" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
232 | 'url-fail04': ('{% load url from future %}{% url "view" id, %}', {}, template.TemplateSyntaxError),
|
---|
233 | 'url-fail05': ('{% load url from future %}{% url "view" id= %}', {}, template.TemplateSyntaxError),
|
---|
234 | 'url-fail06': ('{% load url from future %}{% url "view" a.id=id %}', {}, template.TemplateSyntaxError),
|
---|
235 | @@ -1531,9 +1543,9 @@
|
---|
236 | 'url-fail08': ('{% load url from future %}{% url "view" id="unterminatedstring %}', {}, template.TemplateSyntaxError),
|
---|
237 | 'url-fail09': ('{% load url from future %}{% url "view" id=", %}', {}, template.TemplateSyntaxError),
|
---|
238 |
|
---|
239 | - 'url-fail11': ('{% load url from future %}{% url named_url %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
240 | - 'url-fail12': ('{% load url from future %}{% url named_url %}', {'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
241 | - 'url-fail13': ('{% load url from future %}{% url named_url %}', {'named_url': 'regressiontests.templates.views.client'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
|
---|
242 | + 'url-fail11': ('{% load url from future %}{% url named_url %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
243 | + 'url-fail12': ('{% load url from future %}{% url named_url %}', {'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
244 | + 'url-fail13': ('{% load url from future %}{% url named_url %}', {'named_url': 'regressiontests.templates.views.client'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
|
---|
245 | 'url-fail14': ('{% load url from future %}{% url named_url id, %}', {'named_url': 'view'}, template.TemplateSyntaxError),
|
---|
246 | 'url-fail15': ('{% load url from future %}{% url named_url id= %}', {'named_url': 'view'}, template.TemplateSyntaxError),
|
---|
247 | 'url-fail16': ('{% load url from future %}{% url named_url a.id=id %}', {'named_url': 'view'}, template.TemplateSyntaxError),
|
---|
248 | Index: tests/regressiontests/templates/nodelist.py
|
---|
249 | ===================================================================
|
---|
250 | --- tests/regressiontests/templates/nodelist.py (revision 16772)
|
---|
251 | +++ tests/regressiontests/templates/nodelist.py (working copy)
|
---|
252 | @@ -2,6 +2,7 @@
|
---|
253 | from django.template import VariableNode, Context, TemplateSyntaxError
|
---|
254 | from django.template.loader import get_template_from_string
|
---|
255 | from django.utils.unittest import TestCase
|
---|
256 | +from django.test.utils import override_settings
|
---|
257 |
|
---|
258 | class NodelistTest(TestCase):
|
---|
259 |
|
---|
260 | @@ -35,19 +36,16 @@
|
---|
261 | Checks whether index of error is calculated correctly in
|
---|
262 | template debugger in for loops. Refs ticket #5831
|
---|
263 | """
|
---|
264 | - def setUp(self):
|
---|
265 | - self.old_template_debug = settings.TEMPLATE_DEBUG
|
---|
266 | - settings.TEMPLATE_DEBUG = True
|
---|
267 | -
|
---|
268 | - def tearDown(self):
|
---|
269 | - settings.TEMPLATE_DEBUG = self.old_template_debug
|
---|
270 | -
|
---|
271 | + @override_settings(DEBUG=True, TEMPLATE_DEBUG = True)
|
---|
272 | def test_correct_exception_index(self):
|
---|
273 | - tests = [
|
---|
274 | - ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% endfor %}', (38, 56)),
|
---|
275 | - ('{% load bad_tag %}{% for i in range %}{% for j in range %}{% badsimpletag %}{% endfor %}{% endfor %}', (58, 76)),
|
---|
276 | - ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% for j in range %}Hello{% endfor %}{% endfor %}', (38, 56)),
|
---|
277 | - ('{% load bad_tag %}{% for i in range %}{% for j in five %}{% badsimpletag %}{% endfor %}{% endfor %}', (38, 57)),
|
---|
278 | + '''
|
---|
279 | + 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.
|
---|
280 | + '''
|
---|
281 | + tests = [ #In each case, we have template contents and the lines at which we expect the template error information to occur.
|
---|
282 | + ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% endfor %}', (18, 38)),
|
---|
283 | + ('{% load bad_tag %}{% for i in range %}{% for j in range %}{% badsimpletag %}{% endfor %}{% endfor %}', (18, 38)),
|
---|
284 | + ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% for j in range %}Hello{% endfor %}{% endfor %}', (18, 38)),
|
---|
285 | + ('{% load bad_tag %}{% for i in range %}{% for j in five %}{% badsimpletag %}{% endfor %}{% endfor %}', (18, 38)),
|
---|
286 | ('{% load bad_tag %}{% for j in five %}{% badsimpletag %}{% endfor %}', (18, 37)),
|
---|
287 | ]
|
---|
288 | context = Context({
|
---|
289 | @@ -58,7 +56,7 @@
|
---|
290 | template = get_template_from_string(source)
|
---|
291 | try:
|
---|
292 | template.render(context)
|
---|
293 | - except TemplateSyntaxError, e:
|
---|
294 | - error_source_index = e.source[1]
|
---|
295 | + except (RuntimeError, TypeError), e:
|
---|
296 | + error_source_index = e.django_template_source[1]
|
---|
297 | self.assertEqual(error_source_index,
|
---|
298 | expected_error_source_index)
|
---|