Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
.. currentmodule:: jinja2

Unreleased

- Breaking change. The evaluation order of an exponent expression changed. Now it is more consistent with how it's going in pure python. :issue:`1720` :issue:`1722`


Version 3.1.2
-------------

Expand Down
5 changes: 4 additions & 1 deletion docs/templates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,10 @@ but exists for completeness' sake. The following operators are supported:

``**``
Raise the left operand to the power of the right operand.
``{{ 2**3 }}`` would return ``8``.
``{{ 2**3 }}`` would return ``8``

.. versionchanged:: 3.2
From 3.2, this operator has the same evaluation order as in Python. ``{{ 2 ** 3 ** 2 }}`` will be evaluated as ``2 ** (3 ** 2)``.

Unlike Python, chained pow is evaluated left to right.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidism, should I remove this outdated explanation? Or it will be better to leave it here in order to keep history in docs?

``{{ 3**3**3 }}`` is evaluated as ``(3**3)**3`` in Jinja, but would
Expand Down
3 changes: 2 additions & 1 deletion src/jinja2/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
self.write(", ")
self.visit(node.right, frame)
else:
self.write("(")
self.write("((")
self.visit(node.left, frame)
self.write(")")
self.write(f" {op} ")
self.visit(node.right, frame)

Expand Down
6 changes: 3 additions & 3 deletions src/jinja2/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ def parse_concat(self) -> nodes.Expr:

def parse_math2(self) -> nodes.Expr:
lineno = self.stream.current.lineno
left = self.parse_pow()
left = self.parse_unary()
while self.stream.current.type in ("mul", "div", "floordiv", "mod"):
cls = _math_nodes[self.stream.current.type]
next(self.stream)
Expand All @@ -615,7 +615,7 @@ def parse_math2(self) -> nodes.Expr:

def parse_pow(self) -> nodes.Expr:
lineno = self.stream.current.lineno
left = self.parse_unary()
left = self.parse_primary()
while self.stream.current.type == "pow":
next(self.stream)
right = self.parse_unary()
Expand All @@ -635,7 +635,7 @@ def parse_unary(self, with_filter: bool = True) -> nodes.Expr:
next(self.stream)
node = nodes.Pos(self.parse_unary(False), lineno=lineno)
else:
node = self.parse_primary()
node = self.parse_pow()
node = self.parse_postfix(node)
if with_filter:
node = self.parse_filter_expr(node)
Expand Down
20 changes: 20 additions & 0 deletions tests/test_lexnparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,26 @@ def test_parse_unary(self, env):
tmpl = env.from_string('{{ -foo["bar"]|abs }}')
assert tmpl.render(foo={"bar": 42}) == "42"

def test_negative_pow(self, env):
tmpl = env.from_string("{{ - 1 ** 2 }}")
assert tmpl.render() == "-1"

def test_pow_associative_from_left_to_right(self, env):
tmpl = env.from_string("{{ 2 ** 3 ** 2 }}")
assert tmpl.render() == "512"

Copy link

@bart-nouveau bart-nouveau Sep 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to verify if issue #1720 is fixed, I would add the following tests:

    def test_pow_negative_base(self, env):
        tmpl = env.from_string("{{ (- 1) ** 2 }}")
        assert tmpl.render() == "1"

    def test_pow_negative_base_variable_exponent(self, env):
        tmpl = env.from_string("{{ (- 1) ** x }}", {"x": 2})
        assert tmpl.render() == "1"

    def test_negative_pow_variable_exponent(self, env):
        tmpl = env.from_string("{{ - 1 ** x }}", {"x": 2})
        assert tmpl.render() == "-1"

All the tests seems to pass adding the change suggested here

def test_pow_negative_base(self, env):
tmpl = env.from_string("{{ (- 1) ** 2 }}")
assert tmpl.render() == "1"

def test_pow_negative_base_variable_exponent(self, env):
tmpl = env.from_string("{{ (- 1) ** x }}", {"x": 2})
assert tmpl.render() == "1"

def test_negative_pow_variable_exponent(self, env):
tmpl = env.from_string("{{ - 1 ** x }}", {"x": 2})
assert tmpl.render() == "-1"


class TestLstripBlocks:
def test_lstrip(self, env):
Expand Down