Skip to content

[ruff] Add unnecessary-assign-before-yield (RUF070)#23300

Merged
ntBre merged 6 commits intoastral-sh:mainfrom
anishgirianish:add-ruf070-unnecessary-assign-before-yield
Feb 20, 2026
Merged

[ruff] Add unnecessary-assign-before-yield (RUF070)#23300
ntBre merged 6 commits intoastral-sh:mainfrom
anishgirianish:add-ruf070-unnecessary-assign-before-yield

Conversation

@anishgirianish
Copy link
Contributor

Summary

Closes #13141

Adds a new rule unnecessary-assign-before-yield (RUF070) that detects variable assignments immediately followed by a yield (or yield from) of that variable, where the variable is not referenced anywhere else. This is the yield equivalent of RET504 (unnecessary-assign).

# Before
def gen():
    x = 1
    yield x

# After
def gen():
    yield 1

Unlike return, yield does not exit the function, so the rule only triggers when the binding has exactly one reference (the yielditself). The fix is marked as unsafe for the same reason.

Test Plan

cargo nextest run -p ruff_linter -- RUF070

@astral-sh-bot
Copy link

astral-sh-bot bot commented Feb 15, 2026

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+82 -0 violations, +0 -0 fixes in 11 projects; 45 projects unchanged)

apache/airflow (+22 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ airflow-core/tests/unit/always/test_project_structure.py:273:24: RUF070 Unnecessary assignment to `resource_files` before `yield from` statement
+ dev/breeze/src/airflow_breeze/commands/release_management_commands.py:1465:15: RUF070 Unnecessary assignment to `provider_id` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_bedrock.py:103:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_bedrock.py:200:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_bedrock.py:251:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_bedrock.py:376:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_bedrock.py:410:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_bedrock.py:731:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_comprehend.py:124:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_comprehend.py:222:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_glue.py:546:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_glue.py:704:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
+ providers/amazon/tests/unit/amazon/aws/operators/test_glue.py:832:19: RUF070 Unnecessary assignment to `hook` before `yield` statement
... 9 additional changes omitted for project

apache/superset (+4 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ superset/commands/streaming_export/base.py:237:31: RUF070 Unnecessary assignment to `error_marker` before `yield` statement
+ superset/utils/mock_data.py:281:15: RUF070 Unnecessary assignment to `entity` before `yield` statement
+ tests/integration_tests/fixtures/energy_dashboard.py:63:15: RUF070 Unnecessary assignment to `slices` before `yield` statement
+ tests/integration_tests/security/api_tests.py:92:11: RUF070 Unnecessary assignment to `test_data` before `yield` statement

binary-husky/gpt_academic (+2 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ request_llms/bridge_chatglm4.py:66:15: RUF070 Unnecessary assignment to `response` before `yield` statement
+ request_llms/bridge_internlm.py:194:19: RUF070 Unnecessary assignment to `response` before `yield` statement

ibis-project/ibis (+2 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ ibis/backends/bigquery/tests/system/test_client.py:526:11: RUF070 Unnecessary assignment to `test_table` before `yield` statement
+ ibis/backends/materialize/__init__.py:1210:31: RUF070 Unnecessary assignment to `batch` before `yield` statement

langchain-ai/langchain (+14 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ libs/langchain/langchain_classic/agents/agent.py:1760:20: RUF070 Unnecessary assignment to `iterator` before `yield from` statement
+ libs/langchain/tests/unit_tests/storage/test_filesystem.py:17:15: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain/tests/unit_tests/storage/test_lc_store.py:18:15: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:11:11: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:17:11: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:25:11: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:32:11: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:39:11: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:46:11: RUF070 Unnecessary assignment to `store` before `yield` statement
+ libs/langchain_v1/tests/unit_tests/agents/conftest_store.py:53:11: RUF070 Unnecessary assignment to `store` before `yield` statement
... 4 additional changes omitted for project

lnbits/lnbits (+14 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ lnbits/fiat/paypal.py:263:19: RUF070 Unnecessary assignment to `value` before `yield` statement
+ lnbits/fiat/stripe.py:292:19: RUF070 Unnecessary assignment to `value` before `yield` statement
+ lnbits/wallets/alby.py:198:19: RUF070 Unnecessary assignment to `value` before `yield` statement
+ lnbits/wallets/clnrest.py:427:35: RUF070 Unnecessary assignment to `payment_hash_from_waitanyinvoice` before `yield` statement
+ lnbits/wallets/lndgrpc.py:309:27: RUF070 Unnecessary assignment to `checking_id` before `yield` statement
+ lnbits/wallets/lndrest.py:321:31: RUF070 Unnecessary assignment to `payment_hash` before `yield` statement
+ lnbits/wallets/lnpay.py:150:19: RUF070 Unnecessary assignment to `value` before `yield` statement
+ lnbits/wallets/nwc.py:297:19: RUF070 Unnecessary assignment to `value` before `yield` statement
+ lnbits/wallets/opennode.py:151:19: RUF070 Unnecessary assignment to `value` before `yield` statement
+ lnbits/wallets/zbd.py:171:19: RUF070 Unnecessary assignment to `value` before `yield` statement
... 4 additional changes omitted for project

milvus-io/pymilvus (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ tests/benchmark/conftest.py:95:15: RUF070 Unnecessary assignment to `client` before `yield` statement

pandas-dev/pandas (+5 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ pandas/core/arrays/datetimes.py:702:28: RUF070 Unnecessary assignment to `converted` before `yield from` statement
+ pandas/core/arrays/timedeltas.py:398:28: RUF070 Unnecessary assignment to `converted` before `yield from` statement
+ pandas/core/internals/blocks.py:401:19: RUF070 Unnecessary assignment to `nb` before `yield` statement
+ pandas/core/internals/ops.py:54:19: RUF070 Unnecessary assignment to `info` before `yield` statement
+ pandas/core/window/rolling.py:340:19: RUF070 Unnecessary assignment to `result` before `yield` statement

reflex-dev/reflex (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ reflex/istate/manager/memory.py:85:19: RUF070 Unnecessary assignment to `state` before `yield` statement

rotki/rotki (+15 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ rotkehlchen/tests/exchanges/test_binance.py:795:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_binance.py:802:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_binance.py:809:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_binance.py:816:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_binance.py:985:20: RUF070 Unnecessary assignment to `responses` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_bitfinex.py:1383:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_bitfinex.py:1615:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_bitfinex.py:1777:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_bitfinex.py:404:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
+ rotkehlchen/tests/exchanges/test_bitfinex.py:698:20: RUF070 Unnecessary assignment to `results` before `yield from` statement
... 5 additional changes omitted for project

pytest-dev/pytest (+2 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ testing/test_link_resolve.py:33:15: RUF070 Unnecessary assignment to `filename` before `yield` statement
+ testing/test_link_resolve.py:48:15: RUF070 Unnecessary assignment to `filename` before `yield` statement

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
RUF070 82 82 0 0 0

@amyreese amyreese requested a review from ntBre February 18, 2026 02:07
@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Feb 18, 2026
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

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

Thank you! This looks good to me overall, just a few minor simplification and test suggestions.

Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

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

Nice, thank you! This looks good overall, just a couple remaining questions about the weird yield yield case and the special with handling I saw in RET504.

@anishgirianish
Copy link
Contributor Author

Thanks for the detailed review, @ntBre and @amyreese! I've pushed updates addressing all the feedback:

  • Refactored to a single let chain with slice pattern, shared Expr::Name check, and chained try_set_fix directly
  • Replaced string slicing with token position comparison
  • Renamed stack → visitor, used visit_body instead of manual loop
  • Yield-as-value now flags with parenthesized fix (yield (yield 1)) instead of bailing out
  • Added with statement handling (ported from ReturnVisitor, including contextlib.suppress guard)
  • Added tests for lambda, walrus, yield-as-value, no-space-after-=, and with cases
  • Updated fix safety docs to mention locals() and debuggers

Happy to adjust anything further!

@anishgirianish anishgirianish requested a review from ntBre February 20, 2026 00:50
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

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

Thank you! I pushed one commit reusing the has_conditional_body implementation from flake8_return instead of copying it over. It's slightly unusual to share code across rule modules, but I think it's preferable to duplicating the helper function.

@ntBre ntBre enabled auto-merge (squash) February 20, 2026 14:16
@ntBre ntBre merged commit beda157 into astral-sh:main Feb 20, 2026
41 checks passed
knutwannheden pushed a commit to openrewrite/ruff that referenced this pull request Feb 20, 2026
…23300)

## Summary          

Closes astral-sh#13141
  
Adds a new rule `unnecessary-assign-before-yield` (`RUF070`) that
detects variable assignments immediately followed by a `yield` (or
`yield from`) of that variable, where the variable is not referenced
anywhere else. This is the `yield` equivalent of `RET504`
(`unnecessary-assign`).

  ```python
  # Before
  def gen():
      x = 1
      yield x

  # After
  def gen():
      yield 1
```

 Unlike return, yield does not exit the function, so the rule only triggers when the binding has exactly one reference (the yielditself). The fix is marked as unsafe for the same reason.

## Test Plan

cargo nextest run -p ruff_linter -- RUF070

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RET504 for yield

3 participants