Skip to content

Fix walrus double eval#14446

Draft
shuckc wants to merge 3 commits intopytest-dev:mainfrom
shuckc:fix-walrus-double-eval
Draft

Fix walrus double eval#14446
shuckc wants to merge 3 commits intopytest-dev:mainfrom
shuckc:fix-walrus-double-eval

Conversation

@shuckc
Copy link
Copy Markdown

@shuckc shuckc commented May 8, 2026

Addresses issue #14445

Root cause: The assertion rewriter's visit_NamedExpr returned the ast.NamedExpr node directly, which was then referenced in multiple places in the rewritten AST (the comparison statement, the results tuple for _call_reprcompare, and format expressions). Each reference re-evaluated the walrus operator at runtime, causing side effects to fire multiple times.

Fix (in src/_pytest/assertion/rewrite.py):

  1. visit_NamedExpr: Returns the NamedExpr inline (preserving evaluation order for function args and comparators) but references the target variable for display instead of re-evaluating the expression.
  2. visit_Compare (left side): When comp.left is a NamedExpr, hoists it into a temp variable before processing comparators. This ensures comparators that reference the walrus target see the assigned value
    (fixing the assert (obj := "foo") == f(obj) case).
  3. visit_Compare (comparators): For NamedExpr comparators, uses the target variable Name in results (failure message) instead of the NamedExpr node, preventing re-evaluation in the failure path. Also saves the
    left value in a temp when a walrus will overwrite it (for correct failure messages).
  4. visit_BoolOp: Saves the short-circuit condition in a per-operand temp variable for the explanation path, so walrus modifications to the original variable don't corrupt the failure message.
  5. visit_Assert: Clears variables_overwrite at the start of each assert, preventing stale walrus mappings from leaking between statements.

Note this PR updates the error message emitted by two tasks - I believe they present the value from before, rather than at the time the assertion fails.

TODO:

  • Add additional test cases that show walrus re-evaluation in the error message path.
  • Add Claude Code to the Co-authored-by commit message.

PR Checklist:

  • Include documentation when adding new features.
  • Include new tests or update existing tests when applicable.
  • Allow maintainers to push and squash when merging my commits. Please uncheck this if you prefer to squash the commits yourself.
  • Add text like closes #XYZW to the PR description and/or commits (where XYZW is the issue number). See the github docs for more information.
  • If AI agents were used, they are credited in Co-authored-by commit trailers.
  • Create a new changelog file in the changelog directory, with a name like <ISSUE NUMBER>.<TYPE>.rst. See changelog/README.rst for details.
  • Add yourself to AUTHORS in alphabetical order.

shuckc added 3 commits May 7, 2026 17:23
….NamedExpr node directly, which was then referenced in multiple places in the rewritten AST (the comparison statement, the results tuple for

   _call_reprcompare, and format expressions). Each reference re-evaluated the walrus operator at runtime, causing side effects to fire multiple times.

Fix (in src/_pytest/assertion/rewrite.py):

  1. visit_NamedExpr: Returns the NamedExpr inline (preserving evaluation order for function args and comparators) but references the target variable for display instead of re-evaluating the expression.
  2. visit_Compare (left side): When comp.left is a NamedExpr, hoists it into a temp variable before processing comparators. This ensures comparators that reference the walrus target see the assigned value
  (fixing the assert (obj := "foo") == f(obj) case).
  3. visit_Compare (comparators): For NamedExpr comparators, uses the target variable Name in results (failure message) instead of the NamedExpr node, preventing re-evaluation in the failure path. Also saves the
   left value in a temp when a walrus will overwrite it (for correct failure messages).
  4. visit_BoolOp: Saves the short-circuit condition in a per-operand temp variable for the explanation path, so walrus modifications to the original variable don't corrupt the failure message.
  5. visit_Assert: Clears variables_overwrite at the start of each assert, preventing stale walrus mappings from leaking between statements.

Closes pytest-dev#14445
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant