diff options
author | David Lord <davidism@gmail.com> | 2019-11-20 06:58:20 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-20 06:58:20 -0800 |
commit | 5d33f673ce261220b0a39b1c7a07e0f3bc6f8c64 (patch) | |
tree | 84e6654a720f17b85458120c08e457b4e1863281 | |
parent | c206cc987eb6521e718b84b18ffa8f9dab8266ae (diff) | |
parent | 1f37d5f0d44b62852c2a564f30c0415da412639c (diff) | |
download | jinja2-5d33f673ce261220b0a39b1c7a07e0f3bc6f8c64.tar.gz |
Merge pull request #1105 from areebbeigh/issue-1102
nodes.py: Return on first false in Compare() expr
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | jinja2/nodes.py | 6 | ||||
-rw-r--r-- | tests/test_lexnparse.py | 39 |
3 files changed, 41 insertions, 8 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 31aa480..f572e9a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -77,6 +77,10 @@ Unreleased :issue:`550` - Use :func:`callable` to inject context at runtime for compatibility with Cython compiled functions. :pr:`1108` +- When chained comparisons of constants are evaluated at compile time, + the result follows Python's behavior of returning ``False`` if any + comparison returns ``False``, rather than only the last one. + :issue:`1102` Version 2.10.3 diff --git a/jinja2/nodes.py b/jinja2/nodes.py index 058e3e6..e46aa91 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -768,13 +768,19 @@ class Compare(Expr): def as_const(self, eval_ctx=None): eval_ctx = get_eval_context(self, eval_ctx) result = value = self.expr.as_const(eval_ctx) + try: for op in self.ops: new_value = op.expr.as_const(eval_ctx) result = _cmpop_to_func[op.op](value, new_value) + + if not result: + return False + value = new_value except Exception: raise Impossible() + return result diff --git a/tests/test_lexnparse.py b/tests/test_lexnparse.py index cc941eb..b542282 100644 --- a/tests/test_lexnparse.py +++ b/tests/test_lexnparse.py @@ -323,16 +323,39 @@ class TestSyntax(object): tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}") assert tmpl.render() == '[1, 2]foo' - def test_compare(self, env): - tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|' - '{{ 2 == 2 }}|{{ 1 <= 1 }}') - assert tmpl.render() == 'True|True|True|True|True' + @pytest.mark.parametrize( + ("a", "op", "b"), + [ + (1, ">", 0), + (1, ">=", 1), + (2, "<", 3), + (3, "<=", 4), + (4, "==", 4), + (4, "!=", 5), + ], + ) + def test_compare(self, env, a, op, b): + t = env.from_string("{{ %d %s %d }}" % (a, op, b)) + assert t.render() == "True" def test_compare_parens(self, env): - tmpl = env.from_string( - "{{ i*(j<5) }}" - ) - assert tmpl.render(i=2, j=3) == '2' + t = env.from_string("{{ i * (j < 5) }}") + assert t.render(i=2, j=3) == "2" + + @pytest.mark.parametrize( + ("src", "expect"), + [ + ("{{ 4 < 2 < 3 }}", "False"), + ("{{ a < b < c }}", "False"), + ("{{ 4 > 2 > 3 }}", "False"), + ("{{ a > b > c }}", "False"), + ("{{ 4 > 2 < 3 }}", "True"), + ("{{ a > b < c }}", "True"), + ], + ) + def test_compare_compound(self, env, src, expect): + t = env.from_string(src) + assert t.render(a=4, b=2, c=3) == expect def test_inop(self, env): tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}') |