Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

<!-- Changes that affect Black's stable style -->

- Preserve multiline compound statement headers when `# fmt: skip` is placed on the
colon line (#5117)
- Fix crash when an f-string follows a `# fmt: off` comment inside brackets (#5097)
- Add support for unpacking in comprehensions (PEP 798) and for lazy imports (PEP 810),
both new syntactic features in Python 3.15 (#5048)
Expand Down
43 changes: 21 additions & 22 deletions src/black/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,27 @@ def _generate_ignored_nodes_from_fmt_skip(
if not prev_sibling and parent:
prev_sibling = parent.prev_sibling

if prev_sibling is not None:
if parent is not None and parent.type == syms.suite and leaf.type == token.NEWLINE:
# The `# fmt: skip` is on the colon line of the if/while/def/class/...
# statements. The ignored nodes should be previous siblings of the
# parent suite node. Do this before the generic "same physical line"
# logic so multiline headers are preserved as a whole.
leaf.prefix = ""
parent_sibling = parent.prev_sibling
while parent_sibling is not None and parent_sibling.type != syms.suite:
ignored_nodes.insert(0, parent_sibling)
parent_sibling = parent_sibling.prev_sibling
# Special case for `async_stmt` where the ASYNC token is on the
# grandparent node.
grandparent = parent.parent
if (
grandparent is not None
and grandparent.prev_sibling is not None
and grandparent.prev_sibling.type == token.ASYNC
):
ignored_nodes.insert(0, grandparent.prev_sibling)
yield from iter(ignored_nodes)
elif prev_sibling is not None:
leaf.prefix = leaf.prefix[comment.consumed :]

# Generates the nodes to be ignored by `fmt: skip`.
Expand Down Expand Up @@ -735,27 +755,6 @@ def _generate_ignored_nodes_from_fmt_skip(
ignored_nodes = header_nodes + ignored_nodes

yield from ignored_nodes
elif (
parent is not None and parent.type == syms.suite and leaf.type == token.NEWLINE
):
# The `# fmt: skip` is on the colon line of the if/while/def/class/...
# statements. The ignored nodes should be previous siblings of the
# parent suite node.
leaf.prefix = ""
parent_sibling = parent.prev_sibling
while parent_sibling is not None and parent_sibling.type != syms.suite:
ignored_nodes.insert(0, parent_sibling)
parent_sibling = parent_sibling.prev_sibling
# Special case for `async_stmt` where the ASYNC token is on the
# grandparent node.
grandparent = parent.parent
if (
grandparent is not None
and grandparent.prev_sibling is not None
and grandparent.prev_sibling.type == token.ASYNC
):
ignored_nodes.insert(0, grandparent.prev_sibling)
yield from iter(ignored_nodes)


def is_fmt_on(container: LN, mode: Mode) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions tests/data/cases/fmtskip_class_header.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Body(model.BaseBody[
"Keyword", "For", "While", "Group", "If", "Try", "Var", "Return", "Continue",
"Break", "model.Message", "Error"
]): # fmt: skip
__slots__ = ()
Loading