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
@RonnyPfannschmidt
Copy link
Copy Markdown
Member

whoops - accidental dupe of #14447 on which i also continued to make #14448

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.

2 participants