diff --git a/django/template/debug.py b/django/template/debug.py index b0f48a844e1..d11f1ee2651 100644 --- a/django/template/debug.py +++ b/django/template/debug.py @@ -4,6 +4,7 @@ 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 +43,9 @@ class DebugParser(Parser): 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,25 +64,18 @@ class DebugParser(Parser): 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): @@ -89,12 +83,12 @@ class DebugVariableNode(VariableNode): 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: diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 06838cf21c2..125cd9ed550 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -227,17 +227,15 @@ class ForNode(Node): 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: diff --git a/django/views/debug.py b/django/views/debug.py index 6041930d62f..8c77d709969 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -8,8 +8,7 @@ from django.conf import settings 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 @@ class ExceptionReporter(object): '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 @@ class ExceptionReporter(object): 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 @@ TECHNICAL_500_TEMPLATE = """ {% endif %} {% if template_info %}
-

Template error

+

Error during template rendering

In template {{ template_info.name }}, error at line {{ template_info.line }}

{{ template_info.message }}

diff --git a/tests/regressiontests/templates/nodelist.py b/tests/regressiontests/templates/nodelist.py index 35684242224..b78653683ff 100644 --- a/tests/regressiontests/templates/nodelist.py +++ b/tests/regressiontests/templates/nodelist.py @@ -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 @@ class ErrorIndexTest(TestCase): 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 @@ class ErrorIndexTest(TestCase): 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) diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index c5c65fd2341..f85416563b6 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -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.core import urlresolvers 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 @@ class Templates(unittest.TestCase): 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 @@ class Templates(unittest.TestCase): 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 @@ class Templates(unittest.TestCase): 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,27 +360,31 @@ class Templates(unittest.TestCase): 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 - - old_settings_module = settings.SETTINGS_MODULE - old_template_debug = settings.TEMPLATE_DEBUG - - settings.SETTINGS_MODULE = None - settings.TEMPLATE_DEBUG = True + from django.template import Template, Context 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) + + + @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)) - settings.SETTINGS_MODULE = old_settings_module - settings.TEMPLATE_DEBUG = old_template_debug def test_invalid_block_suggestion(self): # See #7876 @@ -666,7 +672,7 @@ class Templates(unittest.TestCase): # 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 @@ class Templates(unittest.TestCase): # 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 @@ class Templates(unittest.TestCase): ### 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 @@ class Templates(unittest.TestCase): '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 @@ class Templates(unittest.TestCase): # 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 @@ class Templates(unittest.TestCase): # 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 @@ class Templates(unittest.TestCase): '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), diff --git a/tests/regressiontests/views/tests/debug.py b/tests/regressiontests/views/tests/debug.py index fc5325d7d78..712d94cfa7b 100644 --- a/tests/regressiontests/views/tests/debug.py +++ b/tests/regressiontests/views/tests/debug.py @@ -75,7 +75,7 @@ class DebugViewTests(TestCase): 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" %