Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
27 changes: 27 additions & 0 deletions tests/data/cases/fmtskip_class_header.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Body(model.BaseBody[
"Keyword", "For", "While", "Group", "If", "Try", "Var", "Return", "Continue",
"Break", "model.Message", "Error"
]): # fmt: skip
__slots__ = ()


def make_result(
first : int,
second: str,
): # fmt: skip
return first, second


async def fetch_result(
client,
*,
timeout = 1,
): # fmt: skip
return await client.fetch(timeout=timeout)


if (
has_permission( user )
and is_ready( item )
): # fmt: skip
process(item)