Refs #27175 -- Removed exception silencing from the {% include %} template tag.
Per deprecation timeline.
This commit is contained in:
parent
96107e2844
commit
e62165b898
|
@ -1,9 +1,6 @@
|
|||
import logging
|
||||
import posixpath
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
|
||||
from django.utils.deprecation import RemovedInDjango21Warning
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .base import (
|
||||
|
@ -15,8 +12,6 @@ register = Library()
|
|||
|
||||
BLOCK_CONTEXT_KEY = 'block_context'
|
||||
|
||||
logger = logging.getLogger('django.template')
|
||||
|
||||
|
||||
class BlockContext:
|
||||
def __init__(self):
|
||||
|
@ -170,46 +165,27 @@ class IncludeNode(Node):
|
|||
in render_context to avoid reparsing and loading when used in a for
|
||||
loop.
|
||||
"""
|
||||
try:
|
||||
template = self.template.resolve(context)
|
||||
# Does this quack like a Template?
|
||||
if not callable(getattr(template, 'render', None)):
|
||||
# If not, we'll try our cache, and get_template()
|
||||
template_name = template
|
||||
cache = context.render_context.dicts[0].setdefault(self, {})
|
||||
template = cache.get(template_name)
|
||||
if template is None:
|
||||
template = context.template.engine.get_template(template_name)
|
||||
cache[template_name] = template
|
||||
# Use the base.Template of a backends.django.Template.
|
||||
elif hasattr(template, 'template'):
|
||||
template = template.template
|
||||
values = {
|
||||
name: var.resolve(context)
|
||||
for name, var in self.extra_context.items()
|
||||
}
|
||||
if self.isolated_context:
|
||||
return template.render(context.new(values))
|
||||
with context.push(**values):
|
||||
return template.render(context)
|
||||
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.",
|
||||
template_name,
|
||||
exc_info=True,
|
||||
)
|
||||
return ''
|
||||
template = self.template.resolve(context)
|
||||
# Does this quack like a Template?
|
||||
if not callable(getattr(template, 'render', None)):
|
||||
# If not, try the cache and get_template().
|
||||
template_name = template
|
||||
cache = context.render_context.dicts[0].setdefault(self, {})
|
||||
template = cache.get(template_name)
|
||||
if template is None:
|
||||
template = context.template.engine.get_template(template_name)
|
||||
cache[template_name] = template
|
||||
# Use the base.Template of a backends.django.Template.
|
||||
elif hasattr(template, 'template'):
|
||||
template = template.template
|
||||
values = {
|
||||
name: var.resolve(context)
|
||||
for name, var in self.extra_context.items()
|
||||
}
|
||||
if self.isolated_context:
|
||||
return template.render(context.new(values))
|
||||
with context.push(**values):
|
||||
return template.render(context)
|
||||
|
||||
|
||||
@register.tag('block')
|
||||
|
|
|
@ -711,21 +711,6 @@ available to the included template::
|
|||
|
||||
{% include "name_snippet.html" with greeting="Hi" only %}
|
||||
|
||||
If the included template causes an exception while it's rendered (including
|
||||
if it's missing or has syntax errors), the behavior varies depending on the
|
||||
:class:`template engine's <django.template.Engine>` ``debug`` option (if not
|
||||
set, this option defaults to the value of :setting:`DEBUG`). When debug mode is
|
||||
turned on, an exception like :exc:`~django.template.TemplateDoesNotExist` or
|
||||
:exc:`~django.template.TemplateSyntaxError` will be raised. When debug mode
|
||||
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
|
||||
|
|
|
@ -241,3 +241,6 @@ how to remove usage of these features.
|
|||
passing ``pylibmc`` behavior settings as top-level attributes of ``OPTIONS``.
|
||||
|
||||
* The ``host`` parameter of ``django.utils.http.is_safe_url()`` is removed.
|
||||
|
||||
* Silencing of exceptions raised while rendering the ``{% include %}`` template
|
||||
tag is removed.
|
||||
|
|
|
@ -501,11 +501,6 @@ Log messages related to the rendering of templates.
|
|||
|
||||
* Missing context variables are logged as ``DEBUG`` messages.
|
||||
|
||||
* Uncaught exceptions raised during the rendering of an
|
||||
:ttag:`{% include %} <include>` are logged as ``WARNING`` messages when
|
||||
debug mode is off (helpful since ``{% include %}`` silences the exception and
|
||||
returns an empty string in that case).
|
||||
|
||||
.. _django-db-logger:
|
||||
|
||||
``django.db.backends``
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import warnings
|
||||
|
||||
from django.template import (
|
||||
Context, Engine, TemplateDoesNotExist, TemplateSyntaxError, loader,
|
||||
)
|
||||
from django.test import SimpleTestCase, ignore_warnings
|
||||
from django.utils.deprecation import RemovedInDjango21Warning
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from ..utils import setup
|
||||
from .test_basic import basic_templates
|
||||
|
@ -39,24 +36,8 @@ class IncludeTagTests(SimpleTestCase):
|
|||
@setup({'include04': 'a{% include "nonexistent" %}b'})
|
||||
def test_include04(self):
|
||||
template = self.engine.get_template('include04')
|
||||
|
||||
if self.engine.debug:
|
||||
with self.assertRaises(TemplateDoesNotExist):
|
||||
template.render(Context({}))
|
||||
else:
|
||||
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.",
|
||||
)
|
||||
with self.assertRaises(TemplateDoesNotExist):
|
||||
template.render(Context({}))
|
||||
|
||||
@setup({
|
||||
'include 05': 'template with a space',
|
||||
|
@ -178,48 +159,28 @@ class IncludeTagTests(SimpleTestCase):
|
|||
@setup({'include-error07': '{% include "include-fail1" %}'}, include_fail_templates)
|
||||
def test_include_error07(self):
|
||||
template = self.engine.get_template('include-error07')
|
||||
|
||||
if self.engine.debug:
|
||||
with self.assertRaises(RuntimeError):
|
||||
template.render(Context())
|
||||
else:
|
||||
with ignore_warnings(category=RemovedInDjango21Warning):
|
||||
self.assertEqual(template.render(Context()), '')
|
||||
with self.assertRaises(RuntimeError):
|
||||
template.render(Context())
|
||||
|
||||
@setup({'include-error08': '{% include "include-fail2" %}'}, include_fail_templates)
|
||||
def test_include_error08(self):
|
||||
template = self.engine.get_template('include-error08')
|
||||
|
||||
if self.engine.debug:
|
||||
with self.assertRaises(TemplateSyntaxError):
|
||||
template.render(Context())
|
||||
else:
|
||||
with ignore_warnings(category=RemovedInDjango21Warning):
|
||||
self.assertEqual(template.render(Context()), '')
|
||||
with self.assertRaises(TemplateSyntaxError):
|
||||
template.render(Context())
|
||||
|
||||
@setup({'include-error09': '{% include failed_include %}'}, include_fail_templates)
|
||||
def test_include_error09(self):
|
||||
context = Context({'failed_include': 'include-fail1'})
|
||||
template = self.engine.get_template('include-error09')
|
||||
|
||||
if self.engine.debug:
|
||||
with self.assertRaises(RuntimeError):
|
||||
template.render(context)
|
||||
else:
|
||||
with ignore_warnings(category=RemovedInDjango21Warning):
|
||||
self.assertEqual(template.render(context), '')
|
||||
with self.assertRaises(RuntimeError):
|
||||
template.render(context)
|
||||
|
||||
@setup({'include-error10': '{% include failed_include %}'}, include_fail_templates)
|
||||
def test_include_error10(self):
|
||||
context = Context({'failed_include': 'include-fail2'})
|
||||
template = self.engine.get_template('include-error10')
|
||||
|
||||
if self.engine.debug:
|
||||
with self.assertRaises(TemplateSyntaxError):
|
||||
template.render(context)
|
||||
else:
|
||||
with ignore_warnings(category=RemovedInDjango21Warning):
|
||||
self.assertEqual(template.render(context), '')
|
||||
with self.assertRaises(TemplateSyntaxError):
|
||||
template.render(context)
|
||||
|
||||
|
||||
class IncludeTests(SimpleTestCase):
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import logging
|
||||
|
||||
from django.template import Context, Engine, Variable, VariableDoesNotExist
|
||||
from django.test import SimpleTestCase, ignore_warnings
|
||||
from django.utils.deprecation import RemovedInDjango21Warning
|
||||
from django.template import Engine, Variable, VariableDoesNotExist
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
|
||||
class TestHandler(logging.Handler):
|
||||
|
@ -81,46 +80,3 @@ class VariableResolveLoggingTests(BaseTemplateLoggingTestCase):
|
|||
def test_no_log_when_variable_exists(self):
|
||||
Variable('article.section').resolve({'article': {'section': 'News'}})
|
||||
self.assertIsNone(self.test_handler.log_record)
|
||||
|
||||
|
||||
class IncludeNodeLoggingTests(BaseTemplateLoggingTestCase):
|
||||
loglevel = logging.WARN
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.engine = Engine(loaders=[
|
||||
('django.template.loaders.locmem.Loader', {
|
||||
'child': '{{ raises_exception }}',
|
||||
}),
|
||||
], debug=False)
|
||||
|
||||
def error_method():
|
||||
raise IndexError("some generic exception")
|
||||
|
||||
cls.ctx = Context({'raises_exception': error_method})
|
||||
|
||||
def test_logs_exceptions_during_rendering_with_debug_disabled(self):
|
||||
template = self.engine.from_string('{% include "child" %}')
|
||||
template.name = 'template_name'
|
||||
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 "
|
||||
"'template_name'. Empty string rendered instead."
|
||||
)
|
||||
self.assertIsNotNone(self.test_handler.log_record.exc_info)
|
||||
self.assertEqual(self.test_handler.log_record.levelno, logging.WARN)
|
||||
|
||||
def test_logs_exceptions_during_rendering_with_no_template_name(self):
|
||||
template = self.engine.from_string('{% include "child" %}')
|
||||
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 "
|
||||
"'unknown'. Empty string rendered instead."
|
||||
)
|
||||
self.assertIsNotNone(self.test_handler.log_record.exc_info)
|
||||
self.assertEqual(self.test_handler.log_record.levelno, logging.WARN)
|
||||
|
|
Loading…
Reference in New Issue