From 2ccb3ed243c06935d658f718e8393fe7bde1756d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Wed, 28 Aug 2024 11:56:32 +0100 Subject: [PATCH] Refs #34521 -- Added __slots__ to template Node classes. --- django/contrib/admin/templatetags/base.py | 2 + django/contrib/admin/templatetags/log.py | 3 + .../flatpages/templatetags/flatpages.py | 3 + django/template/base.py | 28 ++++++-- django/template/defaulttags.py | 69 +++++++++++++++++++ django/template/library.py | 6 ++ django/template/loader_tags.py | 13 +++- django/templatetags/cache.py | 9 +++ django/templatetags/i18n.py | 34 +++++++++ django/templatetags/l10n.py | 3 + django/templatetags/static.py | 6 ++ django/templatetags/tz.py | 9 +++ tests/template_tests/templatetags/custom.py | 1 + tests/template_tests/templatetags/testtags.py | 1 + 14 files changed, 181 insertions(+), 6 deletions(-) diff --git a/django/contrib/admin/templatetags/base.py b/django/contrib/admin/templatetags/base.py index 9551c0e71d..236e0a8ec5 100644 --- a/django/contrib/admin/templatetags/base.py +++ b/django/contrib/admin/templatetags/base.py @@ -9,6 +9,8 @@ class InclusionAdminNode(InclusionNode): or globally. """ + __slots__ = ("template_name",) + def __init__(self, parser, token, func, template_name, takes_context=True): self.template_name = template_name params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec( diff --git a/django/contrib/admin/templatetags/log.py b/django/contrib/admin/templatetags/log.py index 55b2c46fa5..8ec72da13c 100644 --- a/django/contrib/admin/templatetags/log.py +++ b/django/contrib/admin/templatetags/log.py @@ -4,7 +4,10 @@ register = template.Library() class AdminLogNode(template.Node): + __slots__ = ("limit", "varname", "user") + def __init__(self, limit, varname, user): + super().__init__() self.limit = limit self.varname = varname self.user = user diff --git a/django/contrib/flatpages/templatetags/flatpages.py b/django/contrib/flatpages/templatetags/flatpages.py index 9ec6b8808a..a44432f4c7 100644 --- a/django/contrib/flatpages/templatetags/flatpages.py +++ b/django/contrib/flatpages/templatetags/flatpages.py @@ -7,7 +7,10 @@ register = template.Library() class FlatpageNode(template.Node): + __slots__ = ("context_name", "starts_with", "user") + def __init__(self, context_name, starts_with=None, user=None): + super().__init__() self.context_name = context_name if starts_with: self.starts_with = template.Variable(starts_with) diff --git a/django/template/base.py b/django/template/base.py index ee2e145c04..d51e0c5891 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -290,6 +290,8 @@ def linebreak_iter(template_source): class Token: + __slots__ = ("token_type", "contents", "position", "lineno") + def __init__(self, token_type, contents, position=None, lineno=None): """ A token representing a string from the template. @@ -338,6 +340,8 @@ class Token: class Lexer: + __slots__ = ("template_string", "verbatim") + def __init__(self, template_string): self.template_string = template_string self.verbatim = False @@ -399,6 +403,8 @@ class Lexer: class DebugLexer(Lexer): + __slots__ = () + def _tag_re_split_positions(self): last = 0 for match in tag_re.finditer(self.template_string): @@ -950,11 +956,16 @@ class Variable: class Node: + __slots__ = ("token", "origin") + # Set this to True for nodes that must be first in the template (although # they can be preceded by text nodes. must_be_first = False child_nodelists = ("nodelist",) - token = None + + def __init__(self): + self.token = None + self.origin = None def render(self, context): """ @@ -1004,9 +1015,13 @@ class Node: class NodeList(list): - # Set to True the first time a non-TextNode is inserted by - # extend_nodelist(). - contains_nontext = False + __slots__ = ("contains_nontext",) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Set to True the first time a non-TextNode is inserted by + # extend_nodelist(). + self.contains_nontext = False def render(self, context): return SafeString("".join([node.render_annotated(context) for node in self])) @@ -1020,9 +1035,12 @@ class NodeList(list): class TextNode(Node): + __slots__ = ("s",) + child_nodelists = () def __init__(self, s): + super().__init__() self.s = s def __repr__(self): @@ -1058,9 +1076,11 @@ def render_value_in_context(value, context): class VariableNode(Node): + __slots__ = ("filter_expression",) child_nodelists = () def __init__(self, filter_expression): + super().__init__() self.filter_expression = filter_expression def __repr__(self): diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index ae74679ec6..63e1339e9c 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -45,7 +45,10 @@ register = Library() class AutoEscapeControlNode(Node): """Implement the actions of the autoescape tag.""" + __slots__ = ("setting", "nodelist") + def __init__(self, setting, nodelist): + super().__init__() self.setting = setting self.nodelist = nodelist @@ -61,6 +64,7 @@ class AutoEscapeControlNode(Node): class CommentNode(Node): + __slots__ = () child_nodelists = () def render(self, context): @@ -68,6 +72,7 @@ class CommentNode(Node): class CsrfTokenNode(Node): + __slots__ = () child_nodelists = () def render(self, context): @@ -93,7 +98,10 @@ class CsrfTokenNode(Node): class CycleNode(Node): + __slots__ = ("cyclevars", "variable_name", "silent") + def __init__(self, cyclevars, variable_name=None, silent=False): + super().__init__() self.cyclevars = cyclevars self.variable_name = variable_name self.silent = silent @@ -118,6 +126,8 @@ class CycleNode(Node): class DebugNode(Node): + __slots__ = () + def render(self, context): if not settings.DEBUG: return "" @@ -131,7 +141,13 @@ class DebugNode(Node): class FilterNode(Node): + __slots__ = ( + "filter_expr", + "nodelist", + ) + def __init__(self, filter_expr, nodelist): + super().__init__() self.filter_expr = filter_expr self.nodelist = nodelist @@ -143,7 +159,10 @@ class FilterNode(Node): class FirstOfNode(Node): + __slots__ = ("vars", "asvar") + def __init__(self, variables, asvar=None): + super().__init__() self.vars = variables self.asvar = asvar @@ -161,11 +180,19 @@ class FirstOfNode(Node): class ForNode(Node): + __slots__ = ( + "loopvars", + "sequence", + "is_reversed", + "nodelist_loop", + "nodelist_empty", + ) child_nodelists = ("nodelist_loop", "nodelist_empty") def __init__( self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None ): + super().__init__() self.loopvars = loopvars self.sequence = sequence self.is_reversed = is_reversed @@ -251,9 +278,11 @@ class ForNode(Node): class IfChangedNode(Node): + __slots__ = ("nodelist_true", "nodelist_false", "_varlist") child_nodelists = ("nodelist_true", "nodelist_false") def __init__(self, nodelist_true, nodelist_false, *varlist): + super().__init__() self.nodelist_true = nodelist_true self.nodelist_false = nodelist_false self._varlist = varlist @@ -299,7 +328,10 @@ class IfChangedNode(Node): class IfNode(Node): + __slots__ = ("conditions_nodelists",) + def __init__(self, conditions_nodelists): + super().__init__() self.conditions_nodelists = conditions_nodelists def __repr__(self): @@ -330,7 +362,10 @@ class IfNode(Node): class LoremNode(Node): + __slots__ = ("count", "method", "common") + def __init__(self, count, method, common): + super().__init__() self.count = count self.method = method self.common = common @@ -353,7 +388,10 @@ GroupedResult = namedtuple("GroupedResult", ["grouper", "list"]) class RegroupNode(Node): + __slots__ = ("target", "expression", "var_name") + def __init__(self, target, expression, var_name): + super().__init__() self.target = target self.expression = expression self.var_name = var_name @@ -382,6 +420,8 @@ class RegroupNode(Node): class LoadNode(Node): + __slots__ = () + child_nodelists = () def render(self, context): @@ -389,7 +429,10 @@ class LoadNode(Node): class NowNode(Node): + __slots__ = ("format_string", "asvar") + def __init__(self, format_string, asvar=None): + super().__init__() self.format_string = format_string self.asvar = asvar @@ -405,7 +448,10 @@ class NowNode(Node): class ResetCycleNode(Node): + __slots__ = ("node",) + def __init__(self, node): + super().__init__() self.node = node def render(self, context): @@ -414,7 +460,10 @@ class ResetCycleNode(Node): class SpacelessNode(Node): + __slots__ = ("nodelist",) + def __init__(self, nodelist): + super().__init__() self.nodelist = nodelist def render(self, context): @@ -424,6 +473,8 @@ class SpacelessNode(Node): class TemplateTagNode(Node): + __slots__ = ("tagtype",) + mapping = { "openblock": BLOCK_TAG_START, "closeblock": BLOCK_TAG_END, @@ -436,6 +487,7 @@ class TemplateTagNode(Node): } def __init__(self, tagtype): + super().__init__() self.tagtype = tagtype def render(self, context): @@ -443,9 +495,12 @@ class TemplateTagNode(Node): class URLNode(Node): + __slots__ = ("view_name", "args", "kwargs", "asvar") + child_nodelists = () def __init__(self, view_name, args, kwargs, asvar): + super().__init__() self.view_name = view_name self.args = args self.kwargs = kwargs @@ -492,7 +547,10 @@ class URLNode(Node): class VerbatimNode(Node): + __slots__ = ("content",) + def __init__(self, content): + super().__init__() self.content = content def render(self, context): @@ -500,7 +558,15 @@ class VerbatimNode(Node): class WidthRatioNode(Node): + __slots__ = ( + "val_expr", + "max_expr", + "max_width", + "asvar", + ) + def __init__(self, val_expr, max_expr, max_width, asvar=None): + super().__init__() self.val_expr = val_expr self.max_expr = max_expr self.max_width = max_width @@ -533,7 +599,10 @@ class WidthRatioNode(Node): class WithNode(Node): + __slots__ = ("nodelist", "extra_context") + def __init__(self, var, name, nodelist, extra_context=None): + super().__init__() self.nodelist = nodelist # var and name are legacy attributes, being left in case they are used # by third-party subclasses of this Node. diff --git a/django/template/library.py b/django/template/library.py index 4ee96cea89..11d5012534 100644 --- a/django/template/library.py +++ b/django/template/library.py @@ -211,7 +211,10 @@ class TagHelperNode(Node): function. """ + __slots__ = ("func", "takes_context", "args", "kwargs") + def __init__(self, func, takes_context, args, kwargs): + super().__init__() self.func = func self.takes_context = takes_context self.args = args @@ -226,6 +229,7 @@ class TagHelperNode(Node): class SimpleNode(TagHelperNode): + __slots__ = ("target_var",) child_nodelists = () def __init__(self, func, takes_context, args, kwargs, target_var): @@ -244,6 +248,8 @@ class SimpleNode(TagHelperNode): class InclusionNode(TagHelperNode): + __slots__ = ("filename",) + def __init__(self, func, takes_context, args, kwargs, filename): super().__init__(func, takes_context, args, kwargs) self.filename = filename diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 1874d8c528..7b0372766c 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -40,10 +40,14 @@ class BlockContext: class BlockNode(Node): + __slots__ = ("name", "nodelist", "parent", "context") + def __init__(self, name, nodelist, parent=None): + super().__init__() self.name = name self.nodelist = nodelist self.parent = parent + self.context = None def __repr__(self): return "" % (self.name, self.nodelist) @@ -68,9 +72,9 @@ class BlockNode(Node): return result def super(self): - if not hasattr(self, "context"): + if self.context is None: raise TemplateSyntaxError( - "'%s' object has no attribute 'context'. Did you use " + "'%s' object has no context. Did you use " "{{ block.super }} in a base template?" % self.__class__.__name__ ) render_context = self.context.render_context @@ -83,10 +87,13 @@ class BlockNode(Node): class ExtendsNode(Node): + __slots__ = ("nodelist", "parent_name", "template_dirs", "blocks") + must_be_first = True context_key = "extends_context" def __init__(self, nodelist, parent_name, template_dirs=None): + super().__init__() self.nodelist = nodelist self.parent_name = parent_name self.template_dirs = template_dirs @@ -160,6 +167,8 @@ class ExtendsNode(Node): class IncludeNode(Node): + __slots__ = ("template", "extra_context", "isolated_context") + context_key = "__include_context" def __init__( diff --git a/django/templatetags/cache.py b/django/templatetags/cache.py index 1c48db7ce9..289b586113 100644 --- a/django/templatetags/cache.py +++ b/django/templatetags/cache.py @@ -6,7 +6,16 @@ register = Library() class CacheNode(Node): + __slots__ = ( + "nodelist", + "expire_time_var", + "fragment_name", + "vary_on", + "cache_name", + ) + def __init__(self, nodelist, expire_time_var, fragment_name, vary_on, cache_name): + super().__init__() self.nodelist = nodelist self.expire_time_var = expire_time_var self.fragment_name = fragment_name diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py index b26cc701b4..8178e2943b 100644 --- a/django/templatetags/i18n.py +++ b/django/templatetags/i18n.py @@ -11,7 +11,10 @@ register = Library() class GetAvailableLanguagesNode(Node): + __slots__ = ("variable",) + def __init__(self, variable): + super().__init__() self.variable = variable def render(self, context): @@ -22,7 +25,10 @@ class GetAvailableLanguagesNode(Node): class GetLanguageInfoNode(Node): + __slots__ = ("lang_code", "variable") + def __init__(self, lang_code, variable): + super().__init__() self.lang_code = lang_code self.variable = variable @@ -33,7 +39,10 @@ class GetLanguageInfoNode(Node): class GetLanguageInfoListNode(Node): + __slots__ = ("languages", "variable") + def __init__(self, languages, variable): + super().__init__() self.languages = languages self.variable = variable @@ -52,7 +61,10 @@ class GetLanguageInfoListNode(Node): class GetCurrentLanguageNode(Node): + __slots__ = ("variable",) + def __init__(self, variable): + super().__init__() self.variable = variable def render(self, context): @@ -61,7 +73,10 @@ class GetCurrentLanguageNode(Node): class GetCurrentLanguageBidiNode(Node): + __slots__ = ("variable",) + def __init__(self, variable): + super().__init__() self.variable = variable def render(self, context): @@ -70,9 +85,12 @@ class GetCurrentLanguageBidiNode(Node): class TranslateNode(Node): + __slots__ = ("noop", "asvar", "message_context", "filter_expression") + child_nodelists = () def __init__(self, filter_expression, noop, asvar=None, message_context=None): + super().__init__() self.noop = noop self.asvar = asvar self.message_context = message_context @@ -102,6 +120,18 @@ class TranslateNode(Node): class BlockTranslateNode(Node): + __slots__ = ( + "extra_context", + "singular", + "plural", + "countervar", + "counter", + "message_context", + "trimmed", + "asvar", + "tag_name", + ) + def __init__( self, extra_context, @@ -114,6 +144,7 @@ class BlockTranslateNode(Node): asvar=None, tag_name="blocktranslate", ): + super().__init__() self.extra_context = extra_context self.singular = singular self.plural = plural @@ -205,7 +236,10 @@ class BlockTranslateNode(Node): class LanguageNode(Node): + __slots__ = ("nodelist", "language") + def __init__(self, nodelist, language): + super().__init__() self.nodelist = nodelist self.language = language diff --git a/django/templatetags/l10n.py b/django/templatetags/l10n.py index 93b0e18ebd..2e7829abbe 100644 --- a/django/templatetags/l10n.py +++ b/django/templatetags/l10n.py @@ -21,7 +21,10 @@ def unlocalize(value): class LocalizeNode(Node): + __slots__ = ("nodelist", "use_l10n") + def __init__(self, nodelist, use_l10n): + super().__init__() self.nodelist = nodelist self.use_l10n = use_l10n diff --git a/django/templatetags/static.py b/django/templatetags/static.py index a68e44add2..8b9b2eb71a 100644 --- a/django/templatetags/static.py +++ b/django/templatetags/static.py @@ -9,10 +9,13 @@ register = template.Library() class PrefixNode(template.Node): + __slots__ = ("varname", "name") + def __repr__(self): return "" % self.name def __init__(self, varname=None, name=None): + super().__init__() if name is None: raise template.TemplateSyntaxError( "Prefix nodes must be given a name to return." @@ -93,9 +96,12 @@ def get_media_prefix(parser, token): class StaticNode(template.Node): + __slots__ = ("varname", "path") + child_nodelists = () def __init__(self, varname=None, path=None): + super().__init__() if path is None: raise template.TemplateSyntaxError( "Static template nodes must be given a path to return." diff --git a/django/templatetags/tz.py b/django/templatetags/tz.py index f2cee2d3fe..48b182a94f 100644 --- a/django/templatetags/tz.py +++ b/django/templatetags/tz.py @@ -94,7 +94,10 @@ class LocalTimeNode(Node): Template node class used by ``localtime_tag``. """ + __slots__ = ("nodelist", "use_tz") + def __init__(self, nodelist, use_tz): + super().__init__() self.nodelist = nodelist self.use_tz = use_tz @@ -111,7 +114,10 @@ class TimezoneNode(Node): Template node class used by ``timezone_tag``. """ + __slots__ = ("nodelist", "tz") + def __init__(self, nodelist, tz): + super().__init__() self.nodelist = nodelist self.tz = tz @@ -126,7 +132,10 @@ class GetCurrentTimezoneNode(Node): Template node class used by ``get_current_timezone_tag``. """ + __slots__ = ("variable",) + def __init__(self, variable): + super().__init__() self.variable = variable def render(self, context): diff --git a/tests/template_tests/templatetags/custom.py b/tests/template_tests/templatetags/custom.py index 8d1130ae78..0169f8ab68 100644 --- a/tests/template_tests/templatetags/custom.py +++ b/tests/template_tests/templatetags/custom.py @@ -211,6 +211,7 @@ def counter(parser, token): class CounterNode(template.Node): def __init__(self): + super().__init__() self.count = 0 def render(self, context): diff --git a/tests/template_tests/templatetags/testtags.py b/tests/template_tests/templatetags/testtags.py index 85f76dc912..3c78ff51d8 100644 --- a/tests/template_tests/templatetags/testtags.py +++ b/tests/template_tests/templatetags/testtags.py @@ -5,6 +5,7 @@ register = Library() class EchoNode(Node): def __init__(self, contents): + super().__init__() self.contents = contents def render(self, context):