]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Generalize scoping. This fixes #603
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 7 Jan 2017 14:35:21 +0000 (15:35 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 7 Jan 2017 14:35:50 +0000 (15:35 +0100)
CHANGES
jinja2/compiler.py
jinja2/runtime.py
tests/test_regression.py

diff --git a/CHANGES b/CHANGES
index 7ed12dc82765e6aa7641805def2ed44c5627463a..bd0dc75b1ce356c060197fdded5e8c5ac6d6182e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,11 @@ Version 2.9
   tests in one expression without extra parentheses.  In particular you can
   now write ``foo is divisibleby 2 or foo is divisibleby 3``
   as you would expect.
+- Greatly changed the scoping system to be more consistent with what template
+  designers and developers expect.  There is now no more magic difference
+  between the different include and import constructs.  Context is now always
+  propagated the same way.  The only remaining differences is the defaults
+  for `with context` and `without context`.
 
 Version 2.8.2
 -------------
index 6595d63214c37364f25d9cc51fa95e0645762da2..6b2212e60c79cddde80453009513d5c9b7aa1019 100644 (file)
@@ -866,7 +866,7 @@ class CodeGenerator(NodeVisitor):
         if node.with_context:
             loop = self.environment.is_async and 'async for' or 'for'
             self.writeline('%s event in template.root_render_func('
-                           'template.new_context(context.parent, True, '
+                           'template.new_context(context.get_all(), True, '
                            '%s)):' % (loop, self.dump_local_context(frame)))
         elif self.environment.is_async:
             self.writeline('for event in (await '
@@ -900,7 +900,7 @@ class CodeGenerator(NodeVisitor):
         self.visit(node.template, frame)
         self.write(', %r).' % self.name)
         if node.with_context:
-            self.write('make_module%s(context.parent, True, %s)'
+            self.write('make_module%s(context.get_all(), True, %s)'
                        % (self.environment.is_async and '_async' or '',
                           self.dump_local_context(frame)))
         elif self.environment.is_async:
@@ -918,7 +918,7 @@ class CodeGenerator(NodeVisitor):
         self.visit(node.template, frame)
         self.write(', %r).' % self.name)
         if node.with_context:
-            self.write('make_module%s(context.parent, True, %s)'
+            self.write('make_module%s(context.get_all(), True, %s)'
                        % (self.environment.is_async and '_async' or '',
                           self.dump_local_context(frame)))
         elif self.environment.is_async:
index 43f25063bab129dc81baf0830bb330daf8d98365..95268e5aff05548e5e7e056db1fea9583135d0a6 100644 (file)
@@ -170,9 +170,14 @@ class Context(object):
         return dict((k, self.vars[k]) for k in self.exported_vars)
 
     def get_all(self):
-        """Return a copy of the complete context as dict including the
-        exported variables.
+        """Return the complete context as dict including the exported
+        variables.  For optimizations reasons this might not return an
+        actual copy so be careful with using it.
         """
+        if not self.vars:
+            return self.parent
+        if not self.parent:
+            return self.vars
         return dict(self.parent, **self.vars)
 
     @internalcode
index 4deaebe90a5f3ecf27ac9659a2a902027b9034e4..7d2ad3a800671ba409ffecfa2044680b3f2e6254 100644 (file)
@@ -352,3 +352,38 @@ class TestBug(object):
         ''')
         assert list(map(int, tmpl.render().split())) == \
             [3, 2, 1, 5, 4, 3, 7, 6, 5]
+
+    def test_scopes_and_blocks(self):
+        env = Environment(loader=DictLoader({
+            'a.html': '''
+                {%- set foo = 'bar' -%}
+                {% include 'x.html' -%}
+            ''',
+            'b.html': '''
+                {%- set foo = 'bar' -%}
+                {% block test %}{% include 'x.html' %}{% endblock -%}
+                ''',
+            'c.html': '''
+                {%- set foo = 'bar' -%}
+                {% block test %}{% set foo = foo
+                    %}{% include 'x.html' %}{% endblock -%}
+            ''',
+            'x.html': '''{{ foo }}|{{ test }}'''
+        }))
+
+        a = env.get_template('a.html')
+        b = env.get_template('b.html')
+        c = env.get_template('c.html')
+
+        assert a.render(test='x').strip() == 'bar|x'
+        assert b.render(test='x').strip() == 'bar|x'
+        assert c.render(test='x').strip() == 'bar|x'
+
+    def test_scopes_and_include(self):
+        env = Environment(loader=DictLoader({
+            'include.html': '{{ var }}',
+            'base.html': '{% include "include.html" %}',
+            'child.html': '{% extends "base.html" %}{% set var = 42 %}',
+        }))
+        t = env.get_template('child.html')
+        assert t.render() == '42'