Skip to content

Commit

Permalink
Generalize scoping. This fixes #603
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Jan 7, 2017
1 parent 147bd57 commit d67f0fd
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 5 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------
Expand Down
6 changes: 3 additions & 3 deletions jinja2/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ def visit_Include(self, node, frame):
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 '
Expand Down Expand Up @@ -900,7 +900,7 @@ def visit_Import(self, node, frame):
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:
Expand All @@ -918,7 +918,7 @@ def visit_FromImport(self, node, frame):
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:
Expand Down
9 changes: 7 additions & 2 deletions jinja2/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,14 @@ def get_exported(self):
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
Expand Down
35 changes: 35 additions & 0 deletions tests/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,38 @@ def test_macro_scoping(self, env):
''')
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'

0 comments on commit d67f0fd

Please sign in to comment.