diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 839eacfd95..2ed145c248 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -1,8 +1,10 @@ import logging import posixpath +import warnings from collections import defaultdict from django.utils import six +from django.utils.deprecation import RemovedInDjango21Warning from django.utils.safestring import mark_safe from .base import ( @@ -208,10 +210,17 @@ class IncludeNode(Node): return template.render(context.new(values)) with context.push(**values): return template.render(context) - except Exception: + except Exception as e: if context.template.engine.debug: raise template_name = getattr(context, 'template_name', None) or 'unknown' + warnings.warn( + "Rendering {%% include '%s' %%} raised %s. In Django 2.1, " + "this exception will be raised rather than silenced and " + "rendered as an empty string." % + (template_name, e.__class__.__name__), + RemovedInDjango21Warning, + ) logger.warning( "Exception raised while rendering {%% include %%} for " "template '%s'. Empty string rendered instead.", diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 5d93915dc6..151cdff68d 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -33,6 +33,9 @@ details on these changes. * The ``host`` parameter of ``django.utils.http.is_safe_url()`` will be removed. +* Silencing of exceptions raised while rendering the ``{% include %}`` template + tag will be removed. + .. _deprecation-removed-in-2.0: 2.0 diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 61c36f47fb..6395f97607 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -731,6 +731,11 @@ is turned off, ``{% include %}`` logs a warning to the ``django.template`` logger with the exception that happens while rendering the included template and returns an empty string. +.. deprecated:: 1.11 + + Silencing exceptions raised while rendering the ``{% include %}`` template + tag is deprecated. In Django 2.1, the exception will be raised. + .. note:: The :ttag:`include` tag should be considered as an implementation of "render this subtemplate and include the HTML", not as "parse this diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 0c1e387b66..221f58f291 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -528,3 +528,8 @@ Miscellaneous * The ``host`` parameter of ``django.utils.http.is_safe_url()`` is deprecated in favor of the new ``allowed_hosts`` parameter. + +* Silencing exceptions raised while rendering the + :ttag:`{% include %} ` template tag is deprecated as the behavior is + often more confusing than helpful. In Django 2.1, the exception will be + raised. diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py index fc1554a17e..ebf3e8e7ab 100644 --- a/tests/template_tests/syntax_tests/test_include.py +++ b/tests/template_tests/syntax_tests/test_include.py @@ -1,7 +1,10 @@ +import warnings + from django.template import ( Context, Engine, TemplateDoesNotExist, TemplateSyntaxError, ) -from django.test import SimpleTestCase +from django.test import SimpleTestCase, ignore_warnings +from django.utils.deprecation import RemovedInDjango21Warning from ..utils import setup from .test_basic import basic_templates @@ -41,9 +44,20 @@ class IncludeTagTests(SimpleTestCase): with self.assertRaises(TemplateDoesNotExist): template.render(Context({})) else: - output = template.render(Context({})) + with warnings.catch_warnings(record=True) as warns: + warnings.simplefilter('always') + output = template.render(Context({})) + self.assertEqual(output, "ab") + self.assertEqual(len(warns), 1) + self.assertEqual( + str(warns[0].message), + "Rendering {% include 'include04' %} raised " + "TemplateDoesNotExist. In Django 2.1, this exception will be " + "raised rather than silenced and rendered as an empty string.", + ) + @setup({ 'include 05': 'template with a space', 'include06': '{% include "include 05"%}', @@ -169,7 +183,8 @@ class IncludeTagTests(SimpleTestCase): with self.assertRaises(RuntimeError): template.render(Context()) else: - self.assertEqual(template.render(Context()), '') + with ignore_warnings(category=RemovedInDjango21Warning): + self.assertEqual(template.render(Context()), '') @setup({'include-error08': '{% include "include-fail2" %}'}, include_fail_templates) def test_include_error08(self): @@ -179,7 +194,8 @@ class IncludeTagTests(SimpleTestCase): with self.assertRaises(TemplateSyntaxError): template.render(Context()) else: - self.assertEqual(template.render(Context()), '') + with ignore_warnings(category=RemovedInDjango21Warning): + self.assertEqual(template.render(Context()), '') @setup({'include-error09': '{% include failed_include %}'}, include_fail_templates) def test_include_error09(self): @@ -190,7 +206,8 @@ class IncludeTagTests(SimpleTestCase): with self.assertRaises(RuntimeError): template.render(context) else: - self.assertEqual(template.render(context), '') + with ignore_warnings(category=RemovedInDjango21Warning): + self.assertEqual(template.render(context), '') @setup({'include-error10': '{% include failed_include %}'}, include_fail_templates) def test_include_error10(self): @@ -201,7 +218,8 @@ class IncludeTagTests(SimpleTestCase): with self.assertRaises(TemplateSyntaxError): template.render(context) else: - self.assertEqual(template.render(context), '') + with ignore_warnings(category=RemovedInDjango21Warning): + self.assertEqual(template.render(context), '') class IncludeTests(SimpleTestCase): diff --git a/tests/template_tests/test_logging.py b/tests/template_tests/test_logging.py index c231fb28d6..a11af3c02d 100644 --- a/tests/template_tests/test_logging.py +++ b/tests/template_tests/test_logging.py @@ -3,7 +3,8 @@ from __future__ import unicode_literals import logging from django.template import Context, Engine, Variable, VariableDoesNotExist -from django.test import SimpleTestCase +from django.test import SimpleTestCase, ignore_warnings +from django.utils.deprecation import RemovedInDjango21Warning class TestHandler(logging.Handler): @@ -104,7 +105,8 @@ class IncludeNodeLoggingTests(BaseTemplateLoggingTestCase): def test_logs_exceptions_during_rendering_with_debug_disabled(self): template = self.engine.from_string('{% include "child" %}') template.name = 'template_name' - self.assertEqual(template.render(self.ctx), '') + with ignore_warnings(category=RemovedInDjango21Warning): + self.assertEqual(template.render(self.ctx), '') self.assertEqual( self.test_handler.log_record.getMessage(), "Exception raised while rendering {% include %} for template " @@ -115,7 +117,8 @@ class IncludeNodeLoggingTests(BaseTemplateLoggingTestCase): def test_logs_exceptions_during_rendering_with_no_template_name(self): template = self.engine.from_string('{% include "child" %}') - self.assertEqual(template.render(self.ctx), '') + with ignore_warnings(category=RemovedInDjango21Warning): + self.assertEqual(template.render(self.ctx), '') self.assertEqual( self.test_handler.log_record.getMessage(), "Exception raised while rendering {% include %} for template "