Skip to content

fix(fmt: skip): respect skip directive on multi-line compound statements#5114

Closed
armorbreak001 wants to merge 1 commit into
psf:mainfrom
armorbreak001:fix/fmt-skip-multiline-compound-stmt
Closed

fix(fmt: skip): respect skip directive on multi-line compound statements#5114
armorbreak001 wants to merge 1 commit into
psf:mainfrom
armorbreak001:fix/fmt-skip-multiline-compound-stmt

Conversation

@armorbreak001
Copy link
Copy Markdown

@armorbreak001 armorbreak001 commented Apr 22, 2026

Summary

Fixes #5112, closes #5117
fixes #5122, closes #5123

When # fmt: skip is placed on the colon line of a multi-line class (or def/if) statement, Black still reformatted it — collapsing multi-line type parameters onto a single line.

Root cause: In _generate_ignored_nodes_from_fmt_skip(), line 647 unconditionally sets prev_sibling = parent.prev_sibling when the leaf (NEWLINE) has no previous sibling. This causes the function to take the per-sibling traversal path (which stops at any leaf containing a newline in its prefix) instead of the suite-sibling collection path (the elif branch at line 738, which correctly collects ALL previous siblings of the suite node).

For a multi-line class like:

class Foo(Base[\
    "A", "B"\
]):  # fmt: skip

The traversal starts at : and collects ], ), : — then hits ] whose prefix contains \n (end of line), stopping early. The entire class header (class Foo(Base[, type params) is never marked as ignored, so Black reformats it.

Fix: Add parent.type != syms.suite condition on line 646 so that suite NEWLINEs fall through to the elif branch that properly collects all statement header nodes.

Test plan

  • Reproduced issue # fmt: skip regression #5112 with the exact example from the bug report — now left unchanged
  • Verified existing # fmt: skip cases still work (assignments, one-line compounds, etc.)
  • All 210 format tests pass
  • All 19 fmtskip format tests pass
  • All 3 skip-related unit tests pass

When  is placed on the colon line of a multi-line class
(or def/if) statement, Black still reformatted it because the sibling
traversal in _generate_ignored_nodes_from_fmt_skip stopped at the first
leaf containing a newline in its prefix (e.g., the closing  of a
multi-line type parameter list).

Root cause: line 646-647 unconditionally set prev_sibling to
parent.prev_sibling for NEWLINE leaves with no prev_sibling, causing
the function to take the per-sibling traversal path (which stops at
newlines) instead of the suite-sibling collection path (which correctly
collects ALL previous siblings of the suite node).

Fix: skip the prev_sibling override when parent is a suite, so the
elif branch that handles compound statements is reached instead.
@pekkaklarck
Copy link
Copy Markdown

Thanks for taking a look at this and for the quick fix @armorbreak001! I don't know the rules of this project, but in general I believe it would be a good idea to add a new test that reproduces the bug and is fixed by this change.

@cobaltt7
Copy link
Copy Markdown
Collaborator

Thanks @armorbreak001! Yes, please do add a new test case, as well as a changelog entry.

@github-actions
Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (dded6c6) to main (8dcc486):

--preview style: no changes

--stable style: no changes


What is this? | Workflow run | diff-shades documentation

cobaltt7 added a commit to 17X61/black that referenced this pull request May 12, 2026
cobaltt7 added a commit that referenced this pull request May 12, 2026
* Preserve multiline headers with fmt skip

* Add changelog entry for fmt skip fix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Expand fmt skip multiline header tests

* Add more test cases from #5114 and #5123

* Update comments.py

---------

Co-authored-by: xtreme <psycheas@outlook.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: cobalt <61329810+cobaltt7@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fail to parse multiline case statement with # fmt: skip # fmt: skip regression

3 participants