diff options
-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] }}') |