From: Armin Ronacher Date: Sun, 8 Jan 2017 02:17:30 +0000 (+0100) Subject: Implement consistent scoping for sets in loops X-Git-Tag: 2.9.3~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8172db059d9952c29f299addf6cfaea3c0773166;p=thirdparty%2Fjinja.git Implement consistent scoping for sets in loops While technically this applies to any scope and not just for loops it comes up most commonly in the context of for loops. This now defines the behavior for scoping in a way that is consistent but different than it was in the past. There is an ongoing conversation if we should keep it that way or not. References #641 --- diff --git a/jinja2/idtracking.py b/jinja2/idtracking.py index 87ef5107..b00dab8c 100644 --- a/jinja2/idtracking.py +++ b/jinja2/idtracking.py @@ -73,10 +73,22 @@ class Symbols(object): return rv def store(self, name): - # We already have that name locally, so we can just bail + self.stores.add(name) + + # If we have not see the name referenced yet, we need to figure + # out what to set it to. if name not in self.refs: + # If there is a parent scope we check if the name has a + # reference there. If it does it means we might have to alias + # to a variable there. + if self.parent is not None: + outer_ref = self.parent.find_ref(name) + if outer_ref is not None: + self._define_ref(name, load=(VAR_LOAD_ALIAS, outer_ref)) + return + + # Otherwise we can just set it to undefined. self._define_ref(name, load=(VAR_LOAD_UNDEFINED, None)) - self.stores.add(name) def declare_parameter(self, name): self.stores.add(name) diff --git a/tests/test_core_tags.py b/tests/test_core_tags.py index 04380082..9f0cabfb 100644 --- a/tests/test_core_tags.py +++ b/tests/test_core_tags.py @@ -199,6 +199,15 @@ class TestForLoop(object): '{{ a }}|{{ b }}|{{ c }}{% endfor %}') assert tmpl.render() == '1|2|3' + def test_intended_scoping_with_set(self, env): + tmpl = env.from_string('{% for item in seq %}{{ x }}' + '{% set x = item %}{{ x }}{% endfor %}') + assert tmpl.render(x=0, seq=[1, 2, 3]) == '010203' + + tmpl = env.from_string('{% set x = 9 %}{% for item in seq %}{{ x }}' + '{% set x = item %}{{ x }}{% endfor %}') + assert tmpl.render(x=0, seq=[1, 2, 3]) == '919293' + @pytest.mark.core_tags @pytest.mark.if_condition