Skip to content

[flake8-simplify] Detect nested async with under sync parent (SIM117) - #27821

Merged
ntBre merged 3 commits into
astral-sh:mainfrom
VedantMadane:fix/sim117-nested-async-under-sync
Sep 15, 2026
Merged

ntBre merged 3 commits into
astral-sh:mainfrom
VedantMadane:fix/sim117-nested-async-under-sync

Conversation

@VedantMadane

Copy link
Copy Markdown
Contributor

Summary

Fixes a SIM117 false negative when nested async with statements sit under a sync with parent (and the inverse).

Previously, SIM117 skipped any with whose parent was itself a single-statement with, so that nested cases were fixed top-to-bottom. That skip was unconditional, so a sync parent caused nested async bodies to be ignored even though sync/async contexts cannot be merged:

with contextlib.nullcontext() as ctx:
    async with mycontext() as actx1:   # was not flagged
        async with mycontext() as actx2:
            ...

The parent deferral now only applies when the parent can actually combine with the current statement: same async/sync and neither side is an exempt standalone manager (asyncio.timeout, trio cancel scopes, etc.).

Test plan

Fixes #27800

@astral-sh-bot
astral-sh-bot Bot requested a review from ntBre August 18, 2026 06:31
@astral-sh-bot

astral-sh-bot Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

ℹ️ ecosystem check detected linter changes. (+9 -0 violations, +0 -0 fixes in 2 projects; 57 projects unchanged)

prefecthq/prefect (+6 -0 violations, +0 -0 fixes)

+ src/prefect/flow_engine.py:2026:13: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/server/utilities/test_messaging.py:307:9: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/server/utilities/test_messaging.py:321:9: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/server/utilities/test_messaging.py:397:9: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/workers/test_base_worker.py:4876:13: SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/workers/test_base_worker.py:5365:13: SIM117 [*] Use a single `with` statement with multiple contexts instead of nested `with` statements

home-assistant/core (+3 -0 violations, +0 -0 fixes)

+ tests/util/test_timeout.py:126:9: SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/util/test_timeout.py:308:9: SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/util/test_timeout.py:318:9: SIM117 Use a single `with` statement with multiple contexts instead of nested `with` statements

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
SIM117 9 9 0 0 0

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+9 -0 violations, +0 -0 fixes in 2 projects; 57 projects unchanged)

prefecthq/prefect (+6 -0 violations, +0 -0 fixes)

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

+ src/prefect/flow_engine.py:2026:13: multiple-with-statements [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/server/utilities/test_messaging.py:307:9: multiple-with-statements [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/server/utilities/test_messaging.py:321:9: multiple-with-statements [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/server/utilities/test_messaging.py:397:9: multiple-with-statements [*] Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/workers/test_base_worker.py:4876:13: multiple-with-statements Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/workers/test_base_worker.py:5365:13: multiple-with-statements [*] Use a single `with` statement with multiple contexts instead of nested `with` statements

home-assistant/core (+3 -0 violations, +0 -0 fixes)

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

+ tests/util/test_timeout.py:126:9: multiple-with-statements Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/util/test_timeout.py:308:9: multiple-with-statements Use a single `with` statement with multiple contexts instead of nested `with` statements
+ tests/util/test_timeout.py:318:9: multiple-with-statements Use a single `with` statement with multiple contexts instead of nested `with` statements

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
multiple-with-statements 9 9 0 0 0

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks, this looks good overall. I just had a few minor suggestions.

@ntBre ntBre added the bug An issue describing something that isn't working, or a PR that fixes a bug label Aug 18, 2026
@VedantMadane

Copy link
Copy Markdown
Contributor Author

Addressed the review notes:

  1. Extracted mergeable_child and used it for both parent deferral and the nested merge check.
  2. Moved the new regression cases to the end of SIM117.py so existing snapshot line numbers are unchanged.
  3. Added the two-pair case (async def f(): with A(): with B(): async with C(): async with D()) expecting one diagnostic per pair.

cargo test -p ruff_linter --lib path_new_sim117 passes locally.

@VedantMadane
VedantMadane force-pushed the fix/sim117-nested-async-under-sync branch from c7db729 to f20b3a8 Compare August 22, 2026 13:21
SIM117 deferred to a single-statement with parent unconditionally, so
nested async with bodies under a sync with (or the inverse) were never
checked. Only skip the child when the parent can actually combine with
it: same async/sync and neither side is an exempt standalone manager.

Fixes astral-sh#27800

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Factor mergeable_child helper for parent deferral and child merge checks,
move new regression fixtures to end of SIM117.py, and add a two-pair
nested sync/async case.

Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
@VedantMadane
VedantMadane force-pushed the fix/sim117-nested-async-under-sync branch from f20b3a8 to 38d695d Compare September 9, 2026 16:14

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you!

@ntBre ntBre changed the title [flake8-simplify] Detect nested async with under sync parent (SIM117) [flake8-simplify] Detect nested async with under sync parent (SIM117) Sep 15, 2026
@ntBre
ntBre merged commit a94bcd1 into astral-sh:main Sep 15, 2026
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug An issue describing something that isn't working, or a PR that fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SIM117 false negative for nested async context managers beneath a sync with parent

2 participants