Code

Ticket #4529: allowmultipleblocks.diff

File allowmultipleblocks.diff, 7.8 KB (added by Noam Raphael <spam.noam@…>, 7 years ago)
Line 
1Index: django/template/__init__.py
2===================================================================
3--- django/template/__init__.py (revision 5457)
4+++ django/template/__init__.py (working copy)
5@@ -190,6 +190,10 @@
6     def split_contents(self):
7         return list(smart_split(self.contents))
8 
9+    def __cmp__(self, other):
10+        return cmp((self.token_type, self.contents),
11+                   (other.token_type, other.contents))
12+
13 class Lexer(object):
14     def __init__(self, template_string, origin):
15         self.template_string = template_string
16@@ -251,47 +255,56 @@
17         self.tokens = tokens
18         self.tags = {}
19         self.filters = {}
20+        self._token_trackers = {}
21         for lib in builtins:
22             self.add_library(lib)
23 
24-    def parse(self, parse_until=None):
25-        if parse_until is None: parse_until = []
26-        nodelist = self.create_nodelist()
27-        while self.tokens:
28-            token = self.next_token()
29-            if token.token_type == TOKEN_TEXT:
30-                self.extend_nodelist(nodelist, TextNode(token.contents), token)
31-            elif token.token_type == TOKEN_VAR:
32-                if not token.contents:
33-                    self.empty_variable(token)
34-                filter_expression = self.compile_filter(token.contents)
35-                var_node = self.create_variable_node(filter_expression)
36-                self.extend_nodelist(nodelist, var_node,token)
37-            elif token.token_type == TOKEN_BLOCK:
38-                if token.contents in parse_until:
39-                    # put token back on token list so calling code knows why it terminated
40-                    self.prepend_token(token)
41-                    return nodelist
42-                try:
43-                    command = token.contents.split()[0]
44-                except IndexError:
45-                    self.empty_block_tag(token)
46-                # execute callback function for this tag and append resulting node
47-                self.enter_command(command, token)
48-                try:
49-                    compile_func = self.tags[command]
50-                except KeyError:
51-                    self.invalid_block_tag(token, command)
52-                try:
53-                    compiled_result = compile_func(self, token)
54-                except TemplateSyntaxError, e:
55-                    if not self.compile_function_error(token, e):
56-                        raise
57-                self.extend_nodelist(nodelist, compiled_result, token)
58-                self.exit_command()
59-        if parse_until:
60-            self.unclosed_block_tag(parse_until)
61-        return nodelist
62+    def parse(self, parse_until=None, token_tracker=None):
63+        if token_tracker is not None:
64+            self._token_trackers[id(token_tracker)] = token_tracker
65+        try:
66+            if parse_until is None: parse_until = []
67+            nodelist = self.create_nodelist()
68+            while self.tokens:
69+                token = self.next_token()
70+                for x in self._token_trackers.itervalues():
71+                    x.append(token)
72+                if token.token_type == TOKEN_TEXT:
73+                    self.extend_nodelist(nodelist, TextNode(token.contents), token)
74+                elif token.token_type == TOKEN_VAR:
75+                    if not token.contents:
76+                        self.empty_variable(token)
77+                    filter_expression = self.compile_filter(token.contents)
78+                    var_node = self.create_variable_node(filter_expression)
79+                    self.extend_nodelist(nodelist, var_node,token)
80+                elif token.token_type == TOKEN_BLOCK:
81+                    if token.contents in parse_until:
82+                        # put token back on token list so calling code knows why it terminated
83+                        self.prepend_token(token)
84+                        return nodelist
85+                    try:
86+                        command = token.contents.split()[0]
87+                    except IndexError:
88+                        self.empty_block_tag(token)
89+                    # execute callback function for this tag and append resulting node
90+                    self.enter_command(command, token)
91+                    try:
92+                        compile_func = self.tags[command]
93+                    except KeyError:
94+                        self.invalid_block_tag(token, command)
95+                    try:
96+                        compiled_result = compile_func(self, token)
97+                    except TemplateSyntaxError, e:
98+                        if not self.compile_function_error(token, e):
99+                            raise
100+                    self.extend_nodelist(nodelist, compiled_result, token)
101+                    self.exit_command()
102+            if parse_until:
103+                self.unclosed_block_tag(parse_until)
104+            return nodelist
105+        finally:
106+            if token_tracker is not None:
107+                del self._token_trackers[id(token_tracker)]
108 
109     def skip_past(self, endtag):
110         while self.tokens:
111@@ -934,3 +947,4 @@
112 
113 add_to_builtins('django.template.defaulttags')
114 add_to_builtins('django.template.defaultfilters')
115+add_to_builtins('django.template.loader_tags')
116Index: django/template/loader_tags.py
117===================================================================
118--- django/template/loader_tags.py      (revision 5457)
119+++ django/template/loader_tags.py      (working copy)
120@@ -121,18 +121,32 @@
121     if len(bits) != 2:
122         raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
123     block_name = bits[1]
124-    # Keep track of the names of BlockNodes found in this template, so we can
125-    # check for duplication.
126-    try:
127-        if block_name in parser.__loaded_blocks:
128-            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
129-        parser.__loaded_blocks.append(block_name)
130-    except AttributeError: # parser.__loaded_blocks isn't a list yet
131-        parser.__loaded_blocks = [block_name]
132-    nodelist = parser.parse(('endblock', 'endblock %s' % block_name))
133+    # If multiple blocks with the same name are defined in the same template,
134+    # all of them must have the same content. To accomplish this, we save
135+    # in the parser all blocks which were already encountered, together with
136+    # the token list from which each one was constructed. When a new block
137+    # with a name that already appeared is encountered, we check if it has
138+    # the same token list, and if so the previously created block is returned.
139+    tokenlist = []
140+    nodelist = parser.parse(('endblock', 'endblock %s' % block_name),
141+                            token_tracker=tokenlist)
142     parser.delete_first_token()
143-    return BlockNode(block_name, nodelist)
144 
145+    try:
146+        loaded_blocks = parser.__loaded_blocks
147+    except AttributeError:
148+        loaded_blocks = parser.__loaded_blocks = {}
149+
150+    try:
151+        prev_block, prev_tokenlist = loaded_blocks[block_name]
152+    except KeyError:
153+        loaded_blocks[block_name] = BlockNode(block_name, nodelist), tokenlist
154+    else:
155+        if prev_tokenlist != tokenlist:
156+            raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once and doesn't have exactly the same contents in all appearances" % (bits[0], block_name)
157+
158+    return loaded_blocks[block_name][0]
159+
160 def do_extends(parser, token):
161     """
162     Signal that this template extends a parent template.
163Index: django/template/loader.py
164===================================================================
165--- django/template/loader.py   (revision 5457)
166+++ django/template/loader.py   (working copy)
167@@ -115,4 +115,4 @@
168     # If we get here, none of the templates could be loaded
169     raise TemplateDoesNotExist, ', '.join(template_name_list)
170 
171-add_to_builtins('django.template.loader_tags')
172+#add_to_builtins('django.template.loader_tags')