Code

Ticket #2414: 2414.2.diff

File 2414.2.diff, 5.4 KB (added by SmileyChris, 4 years ago)
Line 
1diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
2index b8bc741..b4cf7ec 100644
3--- a/django/template/loader_tags.py
4+++ b/django/template/loader_tags.py
5@@ -1,4 +1,4 @@
6-from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
7+from django.template import TemplateSyntaxError, Variable
8 from django.template import Library, Node, TextNode
9 from django.template.loader import get_template
10 from django.conf import settings
11@@ -7,8 +7,12 @@ from django.utils.safestring import mark_safe
12 register = Library()
13 
14 BLOCK_CONTEXT_KEY = 'block_context'
15+EXTENDS_SEEN_KEY = 'extends_seen_parents'
16 
17-class ExtendsError(Exception):
18+class ExtendsError(TemplateSyntaxError):
19+    pass
20+
21+class ExtendsRecursionError(ExtendsError):
22     pass
23 
24 class BlockContext(object):
25@@ -94,9 +98,18 @@ class ExtendsNode(Node):
26             error_msg = "Invalid template name in 'extends' tag: %r." % parent
27             if self.parent_name_expr:
28                 error_msg += " Got this from the '%s' variable." % self.parent_name_expr.token
29-            raise TemplateSyntaxError(error_msg)
30+            raise ExtendsError(error_msg)
31         if hasattr(parent, 'render'):
32             return parent # parent is a Template object
33+        if EXTENDS_SEEN_KEY not in context.render_context:
34+            seen_parents = set()
35+            context.render_context[EXTENDS_SEEN_KEY] = seen_parents
36+        else:
37+            seen_parents = context.render_context[EXTENDS_SEEN_KEY]
38+            if parent in seen_parents:
39+                raise ExtendsRecursionError('Template %r cannot be extended '
40+                        'because it would cause a circular recursion' % parent)
41+        seen_parents.add(parent)
42         return get_template(parent)
43 
44     def render(self, context):
45@@ -109,8 +122,9 @@ class ExtendsNode(Node):
46         # Add the block nodes from this node to the block context
47         block_context.add_blocks(self.blocks)
48 
49-        # If this block's parent doesn't have an extends node it is the root,
50-        # and its block nodes also need to be added to the block context.
51+        # If this template's parent doesn't have an extends node it is the root
52+        # then the parent's block nodes also need to be added to the block
53+        # context.
54         for node in compiled_parent.nodelist:
55             # The ExtendsNode has to be the first non-text node.
56             if not isinstance(node, TextNode):
57diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
58index 5902e8d..e13623b 100644
59--- a/tests/regressiontests/templates/tests.py
60+++ b/tests/regressiontests/templates/tests.py
61@@ -15,7 +15,7 @@ import unittest
62 
63 from django import template
64 from django.core import urlresolvers
65-from django.template import loader
66+from django.template import loader, loader_tags
67 from django.template.loaders import app_directories, filesystem, cached
68 from django.utils.translation import activate, deactivate, ugettext as _
69 from django.utils.safestring import mark_safe
70@@ -642,7 +642,7 @@ class Templates(unittest.TestCase):
71             'exception01': ("{% extends 'nonexistent' %}", {}, template.TemplateDoesNotExist),
72 
73             # Raise exception for invalid template name (in variable)
74-            'exception02': ("{% extends nonexistent %}", {}, (template.TemplateSyntaxError, template.TemplateDoesNotExist)),
75+            'exception02': ("{% extends nonexistent %}", {}, (loader_tags.ExtendsError, template.TemplateDoesNotExist)),
76 
77             # Raise exception for extra {% extends %} tags
78             'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError),
79@@ -1041,6 +1041,24 @@ class Templates(unittest.TestCase):
80             'inheritance40': ("{% extends 'inheritance33' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': 1}, '1new23'),
81             'inheritance41': ("{% extends 'inheritance36' %}{% block opt %}new{{ block.super }}{% endblock %}", {'numbers': '123'}, '_new1_new2_new3_'),
82 
83+            ### CIRCULAR INHERITANCE EXCEPTIONS #######################################
84+
85+            # Self-extending template
86+            'circularinheritance01': ("{% extends 'circularinheritance01' %}", {}, loader_tags.ExtendsRecursionError),
87+
88+            # Circular extending template
89+            'circularinheritance02': ("{% extends 'circularinheritance04' %}", {}, loader_tags.ExtendsRecursionError),
90+            'circularinheritance03': ("{% extends 'circularinheritance02' %}", {}, loader_tags.ExtendsRecursionError),
91+            'circularinheritance04': ("{% extends 'circularinheritance03' %}", {}, loader_tags.ExtendsRecursionError),
92+
93+            # Self-extending template (by variable)
94+            'circularinheritance05': ("{% extends var %}", {'var': 'circularinheritance05'}, loader_tags.ExtendsRecursionError),
95+
96+            # Circular template (by variable)
97+            'circularinheritance06': ("{% extends var %}", {'var': 'circularinheritance08'}, loader_tags.ExtendsRecursionError),
98+            'circularinheritance07': ("{% extends var %}", {'var': 'circularinheritance06'}, loader_tags.ExtendsRecursionError),
99+            'circularinheritance08': ("{% extends var %}", {'var': 'circularinheritance07'}, loader_tags.ExtendsRecursionError),
100+
101             ### I18N ##################################################################
102 
103             # {% spaceless %} tag