Ticket #16770: unwrap_templatesyntaxerror-1.diff

File unwrap_templatesyntaxerror-1.diff, 17.9 KB (added by jMyles, 13 years ago)

Here's the latest patch. In addition to the code and tests, this one also fixes old tests that expected TemplateSyntaxError.

  • django/views/debug.py

     
    141141                else:
    142142                    # Cleanse only the specified parameters.
    143143                    for param in sensitive_post_parameters:
    144                         if param in cleansed:
     144                        if cleansed.has_key(param):
    145145                            cleansed[param] = CLEANSED_SUBSTITUTE
    146146                    return cleansed
    147147            else:
     
    223223                    'loader': loader_name,
    224224                    'templates': template_list,
    225225                })
    226         if (settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'source') and
    227             isinstance(self.exc_value, TemplateSyntaxError)):
     226        if (settings.TEMPLATE_DEBUG and hasattr(self.exc_value, 'django_template_source')):
    228227            self.get_template_exception_info()
    229228
    230229        frames = self.get_traceback_frames()
     
    268267        return t.render(c)
    269268
    270269    def get_template_exception_info(self):
    271         origin, (start, end) = self.exc_value.source
     270        origin, (start, end) = self.exc_value.django_template_source
    272271        template_source = origin.reload()
    273272        context_lines = 10
    274273        line = 0
     
    626625{% endif %}
    627626{% if template_info %}
    628627<div id="template">
    629    <h2>Template error</h2>
     628   <h2>Error occured during template rendering</h2>
    630629   <p>In template <code>{{ template_info.name }}</code>, error at line <strong>{{ template_info.line }}</strong></p>
    631630   <h3>{{ template_info.message }}</h3>
    632631   <table class="source{% if template_info.top %} cut-top{% endif %}{% ifnotequal template_info.bottom template_info.total %} cut-bottom{% endifnotequal %}">
  • django/template/debug.py

     
    33from django.utils.html import escape
    44from django.utils.safestring import SafeData, EscapeData
    55from django.utils.formats import localize
     6from psycopg2 import OperationalError
    67
     8
    79class DebugLexer(Lexer):
    810    def __init__(self, template_string, origin):
    911        super(DebugLexer, self).__init__(template_string, origin)
     
    4244    def error(self, token, msg):
    4345        return self.source_error(token.source, msg)
    4446
    45     def source_error(self, source,msg):
     47    def source_error(self, source, msg):
    4648        e = TemplateSyntaxError(msg)
    47         e.source = source
     49        e.source_template_node = source #Identify the template node that was being rendered when the error occurred.
    4850        return e
    4951
    5052    def create_nodelist(self):
     
    6769            e.source = token.source
    6870
    6971class DebugNodeList(NodeList):
     72    '''
     73    A list of nodes that is instantiated when debug is True - this is the nerve center of exceptions that occur during template rendering.
     74    '''
    7075    def render_node(self, node, context):
    71         try:
    72             result = node.render(context)
    73         except TemplateSyntaxError, e:
    74             if not hasattr(e, 'source'):
    75                 e.source = node.source
    76             raise
     76        try:           
     77            return node.render(context)
    7778        except Exception, e:
    78             from sys import exc_info
    79             wrapped = TemplateSyntaxError(u'Caught %s while rendering: %s' %
    80                 (e.__class__.__name__, force_unicode(e, errors='replace')))
    81             wrapped.source = getattr(e, 'template_node_source', node.source)
    82             wrapped.exc_info = exc_info()
    83             raise wrapped, None, wrapped.exc_info[2]
    84         return result
     79            if not hasattr(e, 'source_template_node'): #Have we already identified the node where the problem occured?
     80                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.
     81            raise e
     82       
    8583
    8684class DebugVariableNode(VariableNode):
    8785    def render(self, context):
  • tests/regressiontests/templates/tests.py

     
    11# -*- coding: utf-8 -*-
    22from django.conf import settings
     3from django.core.urlresolvers import NoReverseMatch
    34
    45if __name__ == '__main__':
    56    # When running this file in isolation, we need to set up the configuration
     
    2021from django.template import loader
    2122from django.template.loaders import app_directories, filesystem, cached
    2223from django.test.utils import (get_warnings_state, restore_warnings_state,
    23     setup_test_template_loader, restore_template_loaders)
     24    setup_test_template_loader, restore_template_loaders, override_settings)
    2425from django.utils import unittest
    2526from django.utils.formats import date_format
    2627from django.utils.translation import activate, deactivate, ugettext as _
     
    309310            r = None
    310311            try:
    311312                r = tmpl.render(template.Context({}))
    312             except template.TemplateSyntaxError, e:
     313            except template.TemplateDoesNotExist, e:
    313314                settings.TEMPLATE_DEBUG = old_td
    314                 self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
     315                self.assertEqual(e.args[0], 'missing.html')
    315316            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
    316317        finally:
    317318            loader.template_source_loaders = old_loaders
     
    336337            r = None
    337338            try:
    338339                r = tmpl.render(template.Context({}))
    339             except template.TemplateSyntaxError, e:
    340                 self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
     340            except template.TemplateDoesNotExist, e:
     341                self.assertEqual(e.args[0], 'missing.html')
    341342            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
    342343
    343344            # For the cached loader, repeat the test, to ensure the first attempt did not cache a
     
    345346            tmpl = loader.get_template(load_name)
    346347            try:
    347348                tmpl.render(template.Context({}))
    348             except template.TemplateSyntaxError, e:
    349                 self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
     349            except template.TemplateDoesNotExist, e:
     350                self.assertEqual(e.args[0], 'missing.html')
    350351            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
    351352        finally:
    352353            loader.template_source_loaders = old_loaders
     
    370371
    371372        t = Template('{% url will_not_match %}')
    372373        c = Context()
    373         try:
    374             rendered = t.render(c)
    375         except TemplateSyntaxError, e:
    376             # Assert that we are getting the template syntax error and not the
    377             # string encoding error.
    378             self.assertEqual(e.args[0], "Caught NoReverseMatch while rendering: Reverse for 'will_not_match' with arguments '()' and keyword arguments '{}' not found.")
     374        self.assertRaises(NoReverseMatch, t.render, c)
    379375
    380376        settings.SETTINGS_MODULE = old_settings_module
    381377        settings.TEMPLATE_DEBUG = old_template_debug
     378       
     379    @override_settings(DEBUG=True, TEMPLATE_DEBUG = True)
     380    def test_no_wrapped_exception(self):
     381        """
     382        Previously exceptions were wrapped by TemplateSyntaxError.  See #16770
     383        """
     384        from django.template import Template, TemplateSyntaxError
     385        c = Context({'coconuts': lambda: 42 / 0})
     386        t = Template("{{coconuts}}")       
     387        self.assertRaises(ZeroDivisionError, t.render, c) #Dividing by zero ought to cause ZeroDivisionError; we'll instead get TemplateSyntaxError here iff it's been wrapped.
     388       
     389        #Let's also confirm that the exception has the attribute we need to show the template information on the exception page.
     390        try:
     391            t.render(c)
     392        except ZeroDivisionError, e:
     393            self.assertTrue(hasattr(e, 'django_template_source'))
    382394
    383395    def test_invalid_block_suggestion(self):
    384396        # See #7876
     
    666678
    667679            # In methods that raise an exception without a
    668680            # "silent_variable_attribute" set to True, the exception propagates
    669             'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
     681            'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)),
    670682
    671683            # Escaped backslash in argument
    672684            'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'),
     
    695707            # In attribute and dict lookups that raise an unexpected exception
    696708            # without a "silent_variable_attribute" set to True, the exception
    697709            # propagates
    698             'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
    699             'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
     710            'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)),
     711            'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)),
    700712
    701713            ### COMMENT SYNTAX ########################################################
    702714            'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
     
    753765            ### EXCEPTIONS ############################################################
    754766
    755767            # Raise exception for invalid template name
    756             'exception01': ("{% extends 'nonexistent' %}", {}, (template.TemplateDoesNotExist, template.TemplateDoesNotExist, template.TemplateSyntaxError)),
     768            'exception01': ("{% extends 'nonexistent' %}", {}, (template.TemplateDoesNotExist, template.TemplateDoesNotExist)),
    757769
    758770            # Raise exception for invalid template name (in variable)
    759771            'exception02': ("{% extends nonexistent %}", {}, (template.TemplateSyntaxError, template.TemplateDoesNotExist)),
     
    10501062            'include-fail2': ('{% load broken_tag %}', {}, template.TemplateSyntaxError),
    10511063            'include-error07': ('{% include "include-fail1" %}', {}, ('', '', RuntimeError)),
    10521064            'include-error08': ('{% include "include-fail2" %}', {}, ('', '', template.TemplateSyntaxError)),
    1053             'include-error09': ('{% include failed_include %}', {'failed_include': 'include-fail1'}, ('', '', template.TemplateSyntaxError)),
     1065            'include-error09': ('{% include failed_include %}', {'failed_include': 'include-fail1'}, ('', '', RuntimeError)),
    10541066            'include-error10': ('{% include failed_include %}', {'failed_include': 'include-fail2'}, ('', '', template.TemplateSyntaxError)),
    10551067
    10561068
     
    14811493
    14821494            # Failures
    14831495            'old-url-fail01': ('{% url %}', {}, template.TemplateSyntaxError),
    1484             'old-url-fail02': ('{% url no_such_view %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
    1485             'old-url-fail03': ('{% url regressiontests.templates.views.client %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
     1496            'old-url-fail02': ('{% url no_such_view %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
     1497            'old-url-fail03': ('{% url regressiontests.templates.views.client %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
    14861498            'old-url-fail04': ('{% url view id, %}', {}, template.TemplateSyntaxError),
    14871499            'old-url-fail05': ('{% url view id= %}', {}, template.TemplateSyntaxError),
    14881500            'old-url-fail06': ('{% url view a.id=id %}', {}, template.TemplateSyntaxError),
     
    15221534
    15231535            # Failures
    15241536            'url-fail01': ('{% load url from future %}{% url %}', {}, template.TemplateSyntaxError),
    1525             'url-fail02': ('{% load url from future %}{% url "no_such_view" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
    1526             'url-fail03': ('{% load url from future %}{% url "regressiontests.templates.views.client" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
     1537            'url-fail02': ('{% load url from future %}{% url "no_such_view" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
     1538            'url-fail03': ('{% load url from future %}{% url "regressiontests.templates.views.client" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
    15271539            'url-fail04': ('{% load url from future %}{% url "view" id, %}', {}, template.TemplateSyntaxError),
    15281540            'url-fail05': ('{% load url from future %}{% url "view" id= %}', {}, template.TemplateSyntaxError),
    15291541            'url-fail06': ('{% load url from future %}{% url "view" a.id=id %}', {}, template.TemplateSyntaxError),
     
    15311543            'url-fail08': ('{% load url from future %}{% url "view" id="unterminatedstring %}', {}, template.TemplateSyntaxError),
    15321544            'url-fail09': ('{% load url from future %}{% url "view" id=", %}', {}, template.TemplateSyntaxError),
    15331545
    1534             'url-fail11': ('{% load url from future %}{% url named_url %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
    1535             'url-fail12': ('{% load url from future %}{% url named_url %}', {'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
    1536             'url-fail13': ('{% load url from future %}{% url named_url %}', {'named_url': 'regressiontests.templates.views.client'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch, template.TemplateSyntaxError)),
     1546            'url-fail11': ('{% load url from future %}{% url named_url %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
     1547            'url-fail12': ('{% load url from future %}{% url named_url %}', {'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
     1548            'url-fail13': ('{% load url from future %}{% url named_url %}', {'named_url': 'regressiontests.templates.views.client'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)),
    15371549            'url-fail14': ('{% load url from future %}{% url named_url id, %}', {'named_url': 'view'}, template.TemplateSyntaxError),
    15381550            'url-fail15': ('{% load url from future %}{% url named_url id= %}', {'named_url': 'view'}, template.TemplateSyntaxError),
    15391551            'url-fail16': ('{% load url from future %}{% url named_url a.id=id %}', {'named_url': 'view'}, template.TemplateSyntaxError),
  • tests/regressiontests/templates/nodelist.py

     
    22from django.template import VariableNode, Context, TemplateSyntaxError
    33from django.template.loader import get_template_from_string
    44from django.utils.unittest import TestCase
     5from django.test.utils import override_settings
    56
    67class NodelistTest(TestCase):
    78
     
    3536    Checks whether index of error is calculated correctly in
    3637    template debugger in for loops. Refs ticket #5831
    3738    """
    38     def setUp(self):
    39         self.old_template_debug = settings.TEMPLATE_DEBUG
    40         settings.TEMPLATE_DEBUG = True
    41 
    42     def tearDown(self):
    43         settings.TEMPLATE_DEBUG = self.old_template_debug
    44 
     39    @override_settings(DEBUG=True, TEMPLATE_DEBUG = True)
    4540    def test_correct_exception_index(self):
    46         tests = [
    47             ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% endfor %}', (38, 56)),
    48             ('{% load bad_tag %}{% for i in range %}{% for j in range %}{% badsimpletag %}{% endfor %}{% endfor %}', (58, 76)),
    49             ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% for j in range %}Hello{% endfor %}{% endfor %}', (38, 56)),
    50             ('{% load bad_tag %}{% for i in range %}{% for j in five %}{% badsimpletag %}{% endfor %}{% endfor %}', (38, 57)),
     41        '''
     42        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.
     43        '''
     44        tests = [ #In each case, we have template contents and the lines at which we expect the template error information to occur.
     45            ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% endfor %}', (18, 38)),
     46            ('{% load bad_tag %}{% for i in range %}{% for j in range %}{% badsimpletag %}{% endfor %}{% endfor %}', (18, 38)),
     47            ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% for j in range %}Hello{% endfor %}{% endfor %}', (18, 38)),
     48            ('{% load bad_tag %}{% for i in range %}{% for j in five %}{% badsimpletag %}{% endfor %}{% endfor %}', (18, 38)),
    5149            ('{% load bad_tag %}{% for j in five %}{% badsimpletag %}{% endfor %}', (18, 37)),
    5250        ]
    5351        context = Context({
     
    5856            template = get_template_from_string(source)
    5957            try:
    6058                template.render(context)
    61             except TemplateSyntaxError, e:
    62                 error_source_index = e.source[1]
     59            except (RuntimeError, TypeError), e:
     60                error_source_index = e.django_template_source[1]
    6361                self.assertEqual(error_source_index,
    6462                                 expected_error_source_index)
Back to Top