diff --git a/django/template/base.py b/django/template/base.py index 32ca251f77..afdf49cc0e 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -200,16 +200,13 @@ class Template(object): def render(self, context): "Display stage -- can be called many times" - context.render_context.push() - try: + with context.render_context.push_state(self): if context.template is None: with context.bind_template(self): context.template_name = self.name return self._render(context) else: return self._render(context) - finally: - context.render_context.pop() def compile_nodelist(self): """ @@ -960,7 +957,7 @@ class Node(object): return self.render(context) except Exception as e: if context.template.engine.debug and not hasattr(e, 'template_debug'): - e.template_debug = context.template.get_exception_info(e, self.token) + e.template_debug = context.render_context.template.get_exception_info(e, self.token) raise def __iter__(self): diff --git a/django/template/context.py b/django/template/context.py index 1e1c391229..134aac4b36 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -199,6 +199,8 @@ class RenderContext(BaseContext): rendering of other templates as they would if they were stored in the normal template context. """ + template = None + def __iter__(self): for d in self.dicts[-1]: yield d @@ -212,6 +214,17 @@ class RenderContext(BaseContext): def __getitem__(self, key): return self.dicts[-1][key] + @contextmanager + def push_state(self, template): + initial = self.template + self.template = template + self.push() + try: + yield + finally: + self.template = initial + self.pop() + class RequestContext(Context): """ diff --git a/tests/template_tests/templates/27584_child.html b/tests/template_tests/templates/27584_child.html new file mode 100644 index 0000000000..5cbb3485b9 --- /dev/null +++ b/tests/template_tests/templates/27584_child.html @@ -0,0 +1,3 @@ +{% load tag_27584 %} + +{% badtag %}{% endbadtag %} diff --git a/tests/template_tests/templates/27584_parent.html b/tests/template_tests/templates/27584_parent.html new file mode 100644 index 0000000000..c7ab8b6736 --- /dev/null +++ b/tests/template_tests/templates/27584_parent.html @@ -0,0 +1 @@ +{% include "27584_child.html" %} diff --git a/tests/template_tests/templatetags/tag_27584.py b/tests/template_tests/templatetags/tag_27584.py new file mode 100644 index 0000000000..8c675be16b --- /dev/null +++ b/tests/template_tests/templatetags/tag_27584.py @@ -0,0 +1,15 @@ +from django import template + +register = template.Library() + + +@register.tag +def badtag(parser, token): + parser.parse(('endbadtag',)) + parser.delete_first_token() + return BadNode() + + +class BadNode(template.Node): + def render(self, context): + raise template.TemplateSyntaxError('error') diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index b2e4c585b9..50ddee6674 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -114,6 +114,17 @@ class TemplateTests(SimpleTestCase): engine.from_string("{% load bad_tag %}{% badtag %}") self.assertEqual(e.exception.template_debug['during'], '{% badtag %}') + def test_compile_tag_error_27584(self): + engine = Engine( + app_dirs=True, + debug=True, + libraries={'tag_27584': 'template_tests.templatetags.tag_27584'}, + ) + t = engine.get_template('27584_parent.html') + with self.assertRaises(TemplateSyntaxError) as e: + t.render(Context()) + self.assertEqual(e.exception.template_debug['during'], '{% badtag %}') + def test_super_errors(self): """ #18169 -- NoReverseMatch should not be silence in block.super.