1
0
mirror of https://github.com/django/django.git synced 2025-09-10 02:59:34 +00:00

Refs #36559 -- Ran template partial source tests in debug mode only.

Added a warning for accessing PartialTemplate.source when debugging is disabled.
Thanks Sarah Boyce for the idea.
This commit is contained in:
farhan 2025-09-02 14:59:30 -04:00 committed by Jacob Walls
parent f0c05a40d2
commit 3485599ef0
3 changed files with 153 additions and 65 deletions

View File

@ -53,6 +53,7 @@ times with multiple contexts)
import inspect import inspect
import logging import logging
import re import re
import warnings
from enum import Enum from enum import Enum
from django.template.context import BaseContext from django.template.context import BaseContext
@ -329,6 +330,13 @@ class PartialTemplate:
@property @property
def source(self): def source(self):
template = self.origin.loader.get_template(self.origin.template_name) template = self.origin.loader.get_template(self.origin.template_name)
if not template.engine.debug:
warnings.warn(
"PartialTemplate.source is only available when template "
"debugging is enabled.",
RuntimeWarning,
stacklevel=2,
)
return self.find_partial_source(template.source, self.name) return self.find_partial_source(template.source, self.name)
def _render(self, context): def _render(self, context):

View File

@ -4,7 +4,9 @@ from unittest import mock
from django.http import HttpResponse from django.http import HttpResponse
from django.template import ( from django.template import (
Context, Context,
NodeList,
Origin, Origin,
PartialTemplate,
Template, Template,
TemplateDoesNotExist, TemplateDoesNotExist,
TemplateSyntaxError, TemplateSyntaxError,
@ -15,6 +17,8 @@ from django.template.loader import render_to_string
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.urls import path, reverse from django.urls import path, reverse
from .utils import setup
engine = engines["django"] engine = engines["django"]
@ -30,18 +34,30 @@ class PartialTagsTests(TestCase):
def test_template_source_is_correct(self): def test_template_source_is_correct(self):
partial = engine.get_template("partial_examples.html#test-partial") partial = engine.get_template("partial_examples.html#test-partial")
self.assertEqual( msg = (
partial.template.source, "PartialTemplate.source is only available when "
"{% partialdef test-partial %}\nTEST-PARTIAL-CONTENT\n{% endpartialdef %}", "template debugging is enabled."
) )
with self.assertRaisesMessage(RuntimeWarning, msg):
self.assertEqual(
partial.template.source,
"{% partialdef test-partial %}\n"
"TEST-PARTIAL-CONTENT\n"
"{% endpartialdef %}",
)
def test_template_source_inline_is_correct(self): def test_template_source_inline_is_correct(self):
partial = engine.get_template("partial_examples.html#inline-partial") partial = engine.get_template("partial_examples.html#inline-partial")
self.assertEqual( msg = (
partial.template.source, "PartialTemplate.source is only available when "
"{% partialdef inline-partial inline %}\nINLINE-CONTENT\n" "template debugging is enabled."
"{% endpartialdef %}",
) )
with self.assertRaisesMessage(RuntimeWarning, msg):
self.assertEqual(
partial.template.source,
"{% partialdef inline-partial inline %}\nINLINE-CONTENT\n"
"{% endpartialdef %}",
)
def test_full_template_from_loader(self): def test_full_template_from_loader(self):
template = engine.get_template("partial_examples.html") template = engine.get_template("partial_examples.html")
@ -149,6 +165,20 @@ class PartialTagsTests(TestCase):
rendered_content = template_with_partial.render({}) rendered_content = template_with_partial.render({})
self.assertEqual("TEST-PARTIAL-CONTENT", rendered_content.strip()) self.assertEqual("TEST-PARTIAL-CONTENT", rendered_content.strip())
def test_template_source_warning(self):
partial = engine.get_template("partial_examples.html#test-partial")
with self.assertWarnsMessage(
RuntimeWarning,
"PartialTemplate.source is only available when template "
"debugging is enabled.",
):
self.assertEqual(
partial.template.source,
"{% partialdef test-partial %}\n"
"TEST-PARTIAL-CONTENT\n"
"{% endpartialdef %}",
)
class RobustPartialHandlingTests(TestCase): class RobustPartialHandlingTests(TestCase):
@ -219,8 +249,18 @@ class RobustPartialHandlingTests(TestCase):
class FindPartialSourceTests(TestCase): class FindPartialSourceTests(TestCase):
@setup(
{
"partial_source_success_template": (
"{% partialdef test-partial %}\n"
"TEST-PARTIAL-CONTENT\n"
"{% endpartialdef %}\n"
),
},
debug_only=True,
)
def test_find_partial_source_success(self): def test_find_partial_source_success(self):
template = engine.get_template("partial_examples.html").template template = self.engine.get_template("partial_source_success_template")
partial_proxy = template.extra_data["partials"]["test-partial"] partial_proxy = template.extra_data["partials"]["test-partial"]
expected = """{% partialdef test-partial %} expected = """{% partialdef test-partial %}
@ -228,8 +268,18 @@ TEST-PARTIAL-CONTENT
{% endpartialdef %}""" {% endpartialdef %}"""
self.assertEqual(partial_proxy.source.strip(), expected.strip()) self.assertEqual(partial_proxy.source.strip(), expected.strip())
@setup(
{
"partial_source_with_inline_template": (
"{% partialdef inline-partial inline %}\n"
"INLINE-CONTENT\n"
"{% endpartialdef %}\n"
),
},
debug_only=True,
)
def test_find_partial_source_with_inline(self): def test_find_partial_source_with_inline(self):
template = engine.get_template("partial_examples.html").template template = self.engine.get_template("partial_source_with_inline_template")
partial_proxy = template.extra_data["partials"]["inline-partial"] partial_proxy = template.extra_data["partials"]["inline-partial"]
expected = """{% partialdef inline-partial inline %} expected = """{% partialdef inline-partial inline %}
@ -237,38 +287,38 @@ INLINE-CONTENT
{% endpartialdef %}""" {% endpartialdef %}"""
self.assertEqual(partial_proxy.source.strip(), expected.strip()) self.assertEqual(partial_proxy.source.strip(), expected.strip())
def test_find_partial_source_nonexistent_partial(self): @setup(
template = engine.get_template("partial_examples.html").template {
partial_proxy = template.extra_data["partials"]["test-partial"] "empty_partial_template": ("{% partialdef empty %}{% endpartialdef %}"),
},
result = partial_proxy.find_partial_source( debug_only=True,
template.source, "nonexistent-partial" )
)
self.assertEqual(result, "")
def test_find_partial_source_empty_partial(self): def test_find_partial_source_empty_partial(self):
template_source = "{% partialdef empty %}{% endpartialdef %}" template = self.engine.get_template("empty_partial_template")
template = Template(template_source)
partial_proxy = template.extra_data["partials"]["empty"] partial_proxy = template.extra_data["partials"]["empty"]
result = partial_proxy.find_partial_source(template_source, "empty") result = partial_proxy.find_partial_source(template.source, "empty")
self.assertEqual(result, "{% partialdef empty %}{% endpartialdef %}") self.assertEqual(result, "{% partialdef empty %}{% endpartialdef %}")
@setup(
{
"consecutive_partials_template": (
"{% partialdef empty %}{% endpartialdef %}"
"{% partialdef other %}...{% endpartialdef %}"
),
},
debug_only=True,
)
def test_find_partial_source_multiple_consecutive_partials(self): def test_find_partial_source_multiple_consecutive_partials(self):
template = self.engine.get_template("consecutive_partials_template")
template_source = (
"{% partialdef empty %}{% endpartialdef %}"
"{% partialdef other %}...{% endpartialdef %}"
)
template = Template(template_source)
empty_proxy = template.extra_data["partials"]["empty"] empty_proxy = template.extra_data["partials"]["empty"]
other_proxy = template.extra_data["partials"]["other"] other_proxy = template.extra_data["partials"]["other"]
empty_result = empty_proxy.find_partial_source(template_source, "empty") empty_result = empty_proxy.find_partial_source(template.source, "empty")
self.assertEqual(empty_result, "{% partialdef empty %}{% endpartialdef %}") self.assertEqual(empty_result, "{% partialdef empty %}{% endpartialdef %}")
other_result = other_proxy.find_partial_source(template_source, "other") other_result = other_proxy.find_partial_source(template.source, "other")
self.assertEqual(other_result, "{% partialdef other %}...{% endpartialdef %}") self.assertEqual(other_result, "{% partialdef other %}...{% endpartialdef %}")
def test_partials_with_duplicate_names(self): def test_partials_with_duplicate_names(self):
@ -306,28 +356,40 @@ INLINE-CONTENT
): ):
Template(template_source, origin=Origin(name="template.html")) Template(template_source, origin=Origin(name="template.html"))
@setup(
{
"named_end_tag_template": (
"{% partialdef thing %}CONTENT{% endpartialdef thing %}"
),
},
debug_only=True,
)
def test_find_partial_source_supports_named_end_tag(self): def test_find_partial_source_supports_named_end_tag(self):
template_source = "{% partialdef thing %}CONTENT{% endpartialdef thing %}" template = self.engine.get_template("named_end_tag_template")
template = Template(template_source)
partial_proxy = template.extra_data["partials"]["thing"] partial_proxy = template.extra_data["partials"]["thing"]
result = partial_proxy.find_partial_source(template_source, "thing") result = partial_proxy.find_partial_source(template.source, "thing")
self.assertEqual( self.assertEqual(
result, "{% partialdef thing %}CONTENT{% endpartialdef thing %}" result, "{% partialdef thing %}CONTENT{% endpartialdef thing %}"
) )
@setup(
{
"nested_partials_basic_template": (
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef %}"
"{% endpartialdef %}"
),
},
debug_only=True,
)
def test_find_partial_source_supports_nested_partials(self): def test_find_partial_source_supports_nested_partials(self):
template_source = ( template = self.engine.get_template("nested_partials_basic_template")
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef %}"
"{% endpartialdef %}"
)
template = Template(template_source)
empty_proxy = template.extra_data["partials"]["outer"] empty_proxy = template.extra_data["partials"]["outer"]
other_proxy = template.extra_data["partials"]["inner"] other_proxy = template.extra_data["partials"]["inner"]
outer_result = empty_proxy.find_partial_source(template_source, "outer") outer_result = empty_proxy.find_partial_source(template.source, "outer")
self.assertEqual( self.assertEqual(
outer_result, outer_result,
( (
@ -336,21 +398,26 @@ INLINE-CONTENT
), ),
) )
inner_result = other_proxy.find_partial_source(template_source, "inner") inner_result = other_proxy.find_partial_source(template.source, "inner")
self.assertEqual(inner_result, "{% partialdef inner %}...{% endpartialdef %}") self.assertEqual(inner_result, "{% partialdef inner %}...{% endpartialdef %}")
@setup(
{
"nested_partials_named_end_template": (
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef inner %}"
"{% endpartialdef outer %}"
),
},
debug_only=True,
)
def test_find_partial_source_supports_nested_partials_and_named_end_tags(self): def test_find_partial_source_supports_nested_partials_and_named_end_tags(self):
template_source = ( template = self.engine.get_template("nested_partials_named_end_template")
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef inner %}"
"{% endpartialdef outer %}"
)
template = Template(template_source)
empty_proxy = template.extra_data["partials"]["outer"] empty_proxy = template.extra_data["partials"]["outer"]
other_proxy = template.extra_data["partials"]["inner"] other_proxy = template.extra_data["partials"]["inner"]
outer_result = empty_proxy.find_partial_source(template_source, "outer") outer_result = empty_proxy.find_partial_source(template.source, "outer")
self.assertEqual( self.assertEqual(
outer_result, outer_result,
( (
@ -359,23 +426,28 @@ INLINE-CONTENT
), ),
) )
inner_result = other_proxy.find_partial_source(template_source, "inner") inner_result = other_proxy.find_partial_source(template.source, "inner")
self.assertEqual( self.assertEqual(
inner_result, "{% partialdef inner %}...{% endpartialdef inner %}" inner_result, "{% partialdef inner %}...{% endpartialdef inner %}"
) )
@setup(
{
"nested_partials_mixed_end_1_template": (
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef %}"
"{% endpartialdef outer %}"
),
},
debug_only=True,
)
def test_find_partial_source_supports_nested_partials_and_mixed_end_tags_1(self): def test_find_partial_source_supports_nested_partials_and_mixed_end_tags_1(self):
template_source = ( template = self.engine.get_template("nested_partials_mixed_end_1_template")
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef %}"
"{% endpartialdef outer %}"
)
template = Template(template_source)
empty_proxy = template.extra_data["partials"]["outer"] empty_proxy = template.extra_data["partials"]["outer"]
other_proxy = template.extra_data["partials"]["inner"] other_proxy = template.extra_data["partials"]["inner"]
outer_result = empty_proxy.find_partial_source(template_source, "outer") outer_result = empty_proxy.find_partial_source(template.source, "outer")
self.assertEqual( self.assertEqual(
outer_result, outer_result,
( (
@ -384,21 +456,26 @@ INLINE-CONTENT
), ),
) )
inner_result = other_proxy.find_partial_source(template_source, "inner") inner_result = other_proxy.find_partial_source(template.source, "inner")
self.assertEqual(inner_result, "{% partialdef inner %}...{% endpartialdef %}") self.assertEqual(inner_result, "{% partialdef inner %}...{% endpartialdef %}")
@setup(
{
"nested_partials_mixed_end_2_template": (
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef inner %}"
"{% endpartialdef %}"
),
},
debug_only=True,
)
def test_find_partial_source_supports_nested_partials_and_mixed_end_tags_2(self): def test_find_partial_source_supports_nested_partials_and_mixed_end_tags_2(self):
template_source = ( template = self.engine.get_template("nested_partials_mixed_end_2_template")
"{% partialdef outer %}"
"{% partialdef inner %}...{% endpartialdef inner %}"
"{% endpartialdef %}"
)
template = Template(template_source)
empty_proxy = template.extra_data["partials"]["outer"] empty_proxy = template.extra_data["partials"]["outer"]
other_proxy = template.extra_data["partials"]["inner"] other_proxy = template.extra_data["partials"]["inner"]
outer_result = empty_proxy.find_partial_source(template_source, "outer") outer_result = empty_proxy.find_partial_source(template.source, "outer")
self.assertEqual( self.assertEqual(
outer_result, outer_result,
( (
@ -407,7 +484,7 @@ INLINE-CONTENT
), ),
) )
inner_result = other_proxy.find_partial_source(template_source, "inner") inner_result = other_proxy.find_partial_source(template.source, "inner")
self.assertEqual( self.assertEqual(
inner_result, "{% partialdef inner %}...{% endpartialdef inner %}" inner_result, "{% partialdef inner %}...{% endpartialdef inner %}"
) )

View File

@ -9,7 +9,7 @@ ROOT = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_DIR = os.path.join(ROOT, "templates") TEMPLATE_DIR = os.path.join(ROOT, "templates")
def setup(templates, *args, test_once=False): def setup(templates, *args, test_once=False, debug_only=False):
""" """
Runs test method multiple times in the following order: Runs test method multiple times in the following order:
@ -54,11 +54,14 @@ def setup(templates, *args, test_once=False):
self.engine = Engine( self.engine = Engine(
libraries=libraries, libraries=libraries,
loaders=loaders, loaders=loaders,
debug=debug_only,
) )
func(self) func(self)
if test_once: if test_once:
return return
func(self) func(self)
if debug_only:
return
self.engine = Engine( self.engine = Engine(
libraries=libraries, libraries=libraries,