keep async keyword with fmt: skip one-line compound statements - #5311
Open
sahvx655-wq wants to merge 3 commits into
Open
keep async keyword with fmt: skip one-line compound statements#5311sahvx655-wq wants to merge 3 commits into
sahvx655-wq wants to merge 3 commits into
Conversation
the async token sits on the wrapping async_stmt, not on the with/for/def compound node, so it was dropped from the ignored nodes and split onto its own line
sahvx655-wq
force-pushed
the
fmtskip-async-compound
branch
from
August 15, 2026 12:13
cfe9f36 to
e915f85
Compare
for more information, see https://pre-commit.ci
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.
Description
Reported in #5307. Black crashes on a one-line
async with/async for/async defthat has a semicolon-separated body and a trailing# fmt: skip, e.g.async with ctx(): a; b # fmt: skip. The non-async form is fine, and the async form is fine without the semicolon, so it took a while to pin down the combination. The parse error it dies on is misleading (cannot parse: ... async) because it comes from Black's second pass reparsing its own output rather than from the input.Dumping the first-pass output showed the cause:
asynchad been split onto its own line, leavingasync\n with g(): ..., which is not valid Python. When a one-line compound statement carries a semicolon body under# fmt: skip,_get_compound_statement_headergathers the header leaves to keep them inline, but it only walks the children of thewith_stmt/for_stmt/funcdef. For an async statement the tree wraps that node in anasync_stmtand the leadingASYNCtoken is a sibling of the compound node, not a child, so it never made it into the skipped nodes and got reformatted on its own. The colon-line form of# fmt: skipalready handles this via the async_stmt grandparent, but the inline-semicolon path did not. The risk if left as-is is that any file with such a line cannot be formatted at all under--safe, and under--fastBlack would silently emit broken code. Fixed by picking up theasync_stmt's leadingASYNCtoken first; the three async variants are added tofmtskip10.py.Close #5307, closes #5308, closes #5314
Checklist - did you ...
--previewstyle, following the stability policy?CHANGES.mdif necessary?