Repository navigation
fix(rewrite): freeze walrus and starred operands too - #14814
Open
RonnyPfannschmidt wants to merge 2 commits into
Open
RonnyPfannschmidt wants to merge 2 commits into
RonnyPfannschmidt wants to merge 2 commits into
Conversation
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
July 31, 2026 19:04
16b94a1 to
cc6afa4
Compare
This was referenced Jul 31, 2026
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
July 31, 2026 20:18
cc6afa4 to
d271ad5
Compare
This was referenced Jul 31, 2026
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
August 10, 2026 07:12
d271ad5 to
5af71a7
Compare
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
August 20, 2026 04:35
5af71a7 to
fd41d89
Compare
This was referenced Aug 20, 2026
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
4 times, most recently
from
August 26, 2026 10:04
837173f to
338cd67
Compare
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
September 1, 2026 11:54
338cd67 to
6446871
Compare
RonnyPfannschmidt
marked this pull request as ready for review
September 10, 2026 05:14
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
September 15, 2026 11:34
6446871 to
3487074
Compare
7 tasks done
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
September 23, 2026 07:00
3487074 to
70aad30
Compare
nicoddemus
requested changes
Sep 26, 2026
Comment on lines
+1027
to
+1032
| else: | ||
| # Every other operand arrives as a temporary: the visit_* methods | ||
| # hoist what they build, and generic_visit assigns whatever is left | ||
| # -- a literal included -- so a name is all that can reach here. | ||
| assert isinstance(value, ast.Name) | ||
| needs_freeze = value.id in _walrus_targets(later) |
Member
There was a problem hiding this comment.
Claude found an issue here, this crashes with chained comparisons (a < b < c) because in this case value will be ast.BoolOp. Here is a test exercising the regression:
@pytest.mark.parametrize(
"expr",
[
"identity(a < b < c)",
"(a < b < c) == True",
"(a < b < c) + 1 == 2",
],
ids=["call-arg", "compare-left", "binop-left"],
)
def test_chained_compare_as_operand(self, expr: str) -> None:
"""A chained comparison visits to a BoolOp, not a name; it must still rewrite."""
assert_evaluation_order(f"""
def check():
def identity(v):
return v
a, b, c = 1, 2, 3
try:
assert {expr}
except AssertionError:
return "raised", None
return "passed", None
""")This test crashes with:
# Every other operand arrives as a temporary: the visit_* methods
# hoist what they build, and generic_visit assigns whatever is left
# -- a literal included -- so a name is all that can reach here.
> assert isinstance(value, ast.Name), value
^^^^^^^^^^^^^^^^^^^^^^^^^^^
E AssertionError: <ast.BoolOp object at 0x0000016EFEB6B390>
(I added the , value to the assertion for debugging).
This is the suggested change to fix it:
Suggested change
| else: | |
| # Every other operand arrives as a temporary: the visit_* methods | |
| # hoist what they build, and generic_visit assigns whatever is left | |
| # -- a literal included -- so a name is all that can reach here. | |
| assert isinstance(value, ast.Name) | |
| needs_freeze = value.id in _walrus_targets(later) | |
| elif isinstance(value, ast.Name): | |
| needs_freeze = value.id in _walrus_targets(later) | |
| else: | |
| # Anything else is built only from temporaries the visit_* methods | |
| # already hoisted -- e.g. the BoolOp a chained comparison yields -- | |
| # so a later walrus cannot change what it evaluates to. | |
| needs_freeze = False |
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
October 4, 2026 13:06
70aad30 to
25fc1e8
Compare
|
@RonnyPfannschmidt this PR fixes class Page:
def __init__(self, status):
self.status = status
def __repr__(self):
return f"Page({self.status})"
def test_attribute():
assert (p := Page(200)).status == 200 and (p := Page(404)).status == 200On this PR (25fc1e8), on #14822 (b044239) and on 9.1.1 shows
Would you like a PR for this on top of your stack, or would you rather fold it into one of these? |
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
October 11, 2026 07:26
25fc1e8 to
ffea765
Compare
visit_operand() only froze a bare name, so two other unhoisted operands
kept being evaluated after everything that follows them:
assert collect((x := 1), identity(x := 2)) == (1, 2)
assert collect(*items, identity(items := [9])) == (1, [9])
A walrus operator left in place assigns once the enclosing expression is
assembled, which is after the later arguments have run -- so the earlier
argument saw the later assignment. A starred argument hid its value
inside an ast.Starred, where the existing Name check could not see it.
Closes the order-starred-argument group and the remaining
order-call-argument entry in the coverage matrix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eeds visit_operand freezes a walrus operand whenever anything follows it, and a comparison always has at least one comparator -- so by the time visit_Compare looks at its left operand, a NamedExpr has already been copied into a temporary. The special case that did it here can never run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RonnyPfannschmidt
force-pushed
the
ronny/fix-walrus-raw-operands
branch
from
October 14, 2026 20:28
ffea765 to
4be88ec
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #14447 → #14921 → #14813. Its diff includes theirs.
visit_operand()(added in #14447) only froze a bare name, so two other unhoisted operands kept being evaluated after everything that follows them:A walrus operator left in place assigns only once the enclosing expression is assembled, which is after the later arguments have run — so the earlier argument saw the later assignment. A starred argument hid its value inside an
ast.Starred, where the existingNamecheck could not reach it.Lands the
order-starred-argumentcase and the lastorder-call-argumentcase of the coverage matrix in #14813, as passing tests (+2).The starred case was found by the matrix, not by hand — it is what the
order-*axis is for.