summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJake Howard <git@theorangeone.net>2025-11-12 17:41:32 +0000
committerJacob Walls <jacobtylerwalls@gmail.com>2025-12-05 10:06:48 -0500
commit0ac548635eee801d5de49bec482b7b8e1e97ef59 (patch)
treecfa0aa9b73c8d0a5e614fe8c8cc6adbc7d3a2efb /django
parent55888655a269f41025405b9a1bff14117ae58e2a (diff)
Fixed #36728 -- Validated template tag arguments at definition time.
Before, `context` and `content` were validated at compile time.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/admin/templatetags/admin_list.py7
-rw-r--r--django/contrib/admin/templatetags/admin_modify.py4
-rw-r--r--django/contrib/admin/templatetags/base.py13
-rw-r--r--django/template/library.py68
4 files changed, 60 insertions, 32 deletions
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 2100f93566..52aae9e589 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -76,6 +76,7 @@ def pagination(cl):
@register.tag(name="pagination")
def pagination_tag(parser, token):
return InclusionAdminNode(
+ "pagination",
parser,
token,
func=pagination,
@@ -361,6 +362,7 @@ def result_list(cl):
@register.tag(name="result_list")
def result_list_tag(parser, token):
return InclusionAdminNode(
+ "result_list",
parser,
token,
func=result_list,
@@ -481,6 +483,7 @@ def date_hierarchy(cl):
@register.tag(name="date_hierarchy")
def date_hierarchy_tag(parser, token):
return InclusionAdminNode(
+ "date_hierarchy",
parser,
token,
func=date_hierarchy,
@@ -505,6 +508,7 @@ def search_form(cl):
@register.tag(name="search_form")
def search_form_tag(parser, token):
return InclusionAdminNode(
+ "search_form",
parser,
token,
func=search_form,
@@ -537,7 +541,7 @@ def admin_actions(context):
@register.tag(name="admin_actions")
def admin_actions_tag(parser, token):
return InclusionAdminNode(
- parser, token, func=admin_actions, template_name="actions.html"
+ "admin_actions", parser, token, func=admin_actions, template_name="actions.html"
)
@@ -545,6 +549,7 @@ def admin_actions_tag(parser, token):
def change_list_object_tools_tag(parser, token):
"""Display the row of change list object tools."""
return InclusionAdminNode(
+ "change_list_object_tools",
parser,
token,
func=lambda context: context,
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index 0e3046ae5a..c3d2ad01d9 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -51,6 +51,7 @@ def prepopulated_fields_js(context):
@register.tag(name="prepopulated_fields_js")
def prepopulated_fields_js_tag(parser, token):
return InclusionAdminNode(
+ "prepopulated_fields_js",
parser,
token,
func=prepopulated_fields_js,
@@ -115,7 +116,7 @@ def submit_row(context):
@register.tag(name="submit_row")
def submit_row_tag(parser, token):
return InclusionAdminNode(
- parser, token, func=submit_row, template_name="submit_line.html"
+ "submit_row", parser, token, func=submit_row, template_name="submit_line.html"
)
@@ -123,6 +124,7 @@ def submit_row_tag(parser, token):
def change_form_object_tools_tag(parser, token):
"""Display the row of change form object tools."""
return InclusionAdminNode(
+ "change_form_object_tools",
parser,
token,
func=lambda context: context,
diff --git a/django/contrib/admin/templatetags/base.py b/django/contrib/admin/templatetags/base.py
index 3f8290d3b1..c0474135ea 100644
--- a/django/contrib/admin/templatetags/base.py
+++ b/django/contrib/admin/templatetags/base.py
@@ -1,5 +1,6 @@
from inspect import getfullargspec
+from django.template.exceptions import TemplateSyntaxError
from django.template.library import InclusionNode, parse_bits
from django.utils.inspect import lazy_annotations
@@ -10,12 +11,21 @@ class InclusionAdminNode(InclusionNode):
or globally.
"""
- def __init__(self, parser, token, func, template_name, takes_context=True):
+ def __init__(self, name, parser, token, func, template_name, takes_context=True):
self.template_name = template_name
with lazy_annotations():
params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = (
getfullargspec(func)
)
+ if takes_context:
+ if params and params[0] == "context":
+ del params[0]
+ else:
+ function_name = func.__name__
+ raise TemplateSyntaxError(
+ f"{name!r} sets takes_context=True so {function_name!r} "
+ "must have a first argument of 'context'"
+ )
bits = token.split_contents()
args, kwargs = parse_bits(
parser,
@@ -26,7 +36,6 @@ class InclusionAdminNode(InclusionNode):
defaults,
kwonly,
kwonly_defaults,
- takes_context,
bits[0],
)
super().__init__(func, takes_context, args, kwargs, filename=None)
diff --git a/django/template/library.py b/django/template/library.py
index 27af7a6969..0a459c0497 100644
--- a/django/template/library.py
+++ b/django/template/library.py
@@ -123,6 +123,15 @@ class Library:
) = getfullargspec(unwrap(func))
function_name = name or func.__name__
+ if takes_context:
+ if params and params[0] == "context":
+ del params[0]
+ else:
+ raise TemplateSyntaxError(
+ f"{function_name!r} is decorated with takes_context=True so it "
+ "must have a first argument of 'context'"
+ )
+
@wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
@@ -139,7 +148,6 @@ class Library:
defaults,
kwonly,
kwonly_defaults,
- takes_context,
function_name,
)
return SimpleNode(func, takes_context, args, kwargs, target_var)
@@ -182,26 +190,32 @@ class Library:
if end_name is None:
end_name = f"end{function_name}"
- @wraps(func)
- def compile_func(parser, token):
- tag_params = params.copy()
+ if takes_context:
+ if len(params) >= 2 and params[1] == "content":
+ del params[1]
+ else:
+ raise TemplateSyntaxError(
+ f"{function_name!r} is decorated with takes_context=True so"
+ " it must have a first argument of 'context' and a second "
+ "argument of 'content'"
+ )
- if takes_context:
- if len(tag_params) >= 2 and tag_params[1] == "content":
- del tag_params[1]
- else:
- raise TemplateSyntaxError(
- f"{function_name!r} is decorated with takes_context=True so"
- " it must have a first argument of 'context' and a second "
- "argument of 'content'"
- )
- elif tag_params and tag_params[0] == "content":
- del tag_params[0]
+ if params and params[0] == "context":
+ del params[0]
else:
raise TemplateSyntaxError(
- f"'{function_name}' must have a first argument of 'content'"
+ f"{function_name!r} is decorated with takes_context=True so it "
+ "must have a first argument of 'context'"
)
+ elif params and params[0] == "content":
+ del params[0]
+ else:
+ raise TemplateSyntaxError(
+ f"{function_name!r} must have a first argument of 'content'"
+ )
+ @wraps(func)
+ def compile_func(parser, token):
bits = token.split_contents()[1:]
target_var = None
if len(bits) >= 2 and bits[-2] == "as":
@@ -214,13 +228,12 @@ class Library:
args, kwargs = parse_bits(
parser,
bits,
- tag_params,
+ params,
varargs,
varkw,
defaults,
kwonly,
kwonly_defaults,
- takes_context,
function_name,
)
@@ -263,6 +276,15 @@ class Library:
) = getfullargspec(unwrap(func))
function_name = name or func.__name__
+ if takes_context:
+ if params and params[0] == "context":
+ params = params[1:]
+ else:
+ raise TemplateSyntaxError(
+ f"{function_name!r} is decorated with takes_context=True so it "
+ "must have a first argument of 'context'"
+ )
+
@wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
@@ -275,7 +297,6 @@ class Library:
defaults,
kwonly,
kwonly_defaults,
- takes_context,
function_name,
)
return InclusionNode(
@@ -394,7 +415,6 @@ def parse_bits(
defaults,
kwonly,
kwonly_defaults,
- takes_context,
name,
):
"""
@@ -402,14 +422,6 @@ def parse_bits(
particular by detecting syntax errors and by extracting positional and
keyword arguments.
"""
- if takes_context:
- if params and params[0] == "context":
- params = params[1:]
- else:
- raise TemplateSyntaxError(
- "'%s' is decorated with takes_context=True so it must "
- "have a first argument of 'context'" % name
- )
args = []
kwargs = {}
unhandled_params = list(params)