Skip to content

Commit e43cf9f

Browse files
Fix #4730: honor fmt: skip in nested in-clause
1 parent 6b5f6ab commit e43cf9f

3 files changed

Lines changed: 88 additions & 4 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

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

16+
- Fix `# fmt: skip` being ignored in nested `if` expressions with parenthesized `in` clauses (#4903)
1617
- Fix crash when an f-string follows a `# fmt: off` comment inside brackets (#5097)
1718
- Add support for unpacking in comprehensions (PEP 798) and for lazy imports (PEP 810),
1819
both new syntactic features in Python 3.15 (#5048)

src/black/comments.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from black.mode import Mode
88
from black.nodes import (
99
CLOSING_BRACKETS,
10+
OPENING_BRACKETS,
1011
STANDALONE_COMMENT,
1112
STATEMENT,
1213
WHITESPACE,
@@ -448,6 +449,14 @@ def stringify_node(n: LN) -> str:
448449

449450
hidden_value = "".join(parts)
450451
comment_lineno = leaf.lineno - comment.newlines
452+
leaf_is_ignored = any(
453+
ignored is leaf
454+
or (
455+
isinstance(ignored, Node)
456+
and any(child is leaf for child in ignored.leaves())
457+
)
458+
for ignored in ignored_nodes
459+
)
451460

452461
if contains_fmt_directive(comment.value, FMT_OFF):
453462
fmt_off_prefix = ""
@@ -461,7 +470,7 @@ def stringify_node(n: LN) -> str:
461470
standalone_comment_prefix += fmt_off_prefix
462471
hidden_value = comment.value + "\n" + hidden_value
463472

464-
if is_fmt_skip:
473+
if is_fmt_skip and not leaf_is_ignored:
465474
hidden_value += comment.leading_whitespace + comment.value
466475

467476
if hidden_value.endswith("\n"):
@@ -630,6 +639,17 @@ def _get_compound_statement_header(
630639
return header_leaves
631640

632641

642+
def _find_closest_previous_sibling(node: LN) -> LN | None:
643+
"""Find the closest previous sibling by walking up the ancestor chain."""
644+
current: LN | None = node
645+
while current is not None:
646+
prev_sibling = current.prev_sibling
647+
if prev_sibling is not None:
648+
return prev_sibling
649+
current = current.parent
650+
return None
651+
652+
633653
def _generate_ignored_nodes_from_fmt_skip(
634654
leaf: Leaf, comment: ProtoComment, mode: Mode
635655
) -> Iterator[LN]:
@@ -643,12 +663,13 @@ def _generate_ignored_nodes_from_fmt_skip(
643663
if not comments or comment.value != comments[0].value:
644664
return
645665

646-
if not prev_sibling and parent:
666+
if prev_sibling is None and parent is not None:
647667
prev_sibling = parent.prev_sibling
648668

649-
if prev_sibling is not None:
650-
leaf.prefix = leaf.prefix[comment.consumed :]
669+
if prev_sibling is None and comment.type == token.COMMENT:
670+
prev_sibling = _find_closest_previous_sibling(leaf)
651671

672+
if prev_sibling is not None:
652673
# Generates the nodes to be ignored by `fmt: skip`.
653674

654675
# Nodes to ignore are the ones on the same line as the
@@ -669,6 +690,14 @@ def _generate_ignored_nodes_from_fmt_skip(
669690
# or NEWLINE leaves.
670691

671692
current_node = prev_sibling
693+
if (
694+
isinstance(current_node, Leaf)
695+
and current_node.type in OPENING_BRACKETS
696+
and current_node.parent
697+
and current_node.parent.type == syms.atom
698+
):
699+
current_node = current_node.parent
700+
672701
ignored_nodes = [current_node]
673702
if current_node.prev_sibling is None and current_node.parent is not None:
674703
current_node = current_node.parent
@@ -734,6 +763,17 @@ def _generate_ignored_nodes_from_fmt_skip(
734763
if header_nodes:
735764
ignored_nodes = header_nodes + ignored_nodes
736765

766+
leaf_is_ignored = any(
767+
ignored is leaf
768+
or (
769+
isinstance(ignored, Node)
770+
and any(child is leaf for child in ignored.leaves())
771+
)
772+
for ignored in ignored_nodes
773+
)
774+
if not leaf_is_ignored:
775+
leaf.prefix = leaf.prefix[comment.consumed :]
776+
737777
yield from ignored_nodes
738778
elif (
739779
parent is not None and parent.type == syms.suite and leaf.type == token.NEWLINE
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Single fmt: skip in multi-part if-clause
2+
class ClassWithALongName:
3+
Constant1 = 1
4+
Constant2 = 2
5+
Constant3 = 3
6+
7+
8+
def test():
9+
if (
10+
"cond1" == "cond1"
11+
and "cond2" == "cond2"
12+
and 1 in ( # fmt: skip
13+
ClassWithALongName.Constant1,
14+
ClassWithALongName.Constant2,
15+
ClassWithALongName.Constant3,
16+
)
17+
):
18+
return True
19+
return False
20+
21+
22+
# output
23+
24+
25+
# Single fmt: skip in multi-part if-clause
26+
class ClassWithALongName:
27+
Constant1 = 1
28+
Constant2 = 2
29+
Constant3 = 3
30+
31+
32+
def test():
33+
if (
34+
"cond1" == "cond1"
35+
and "cond2" == "cond2"
36+
and 1 in ( # fmt: skip
37+
ClassWithALongName.Constant1,
38+
ClassWithALongName.Constant2,
39+
ClassWithALongName.Constant3,
40+
)
41+
):
42+
return True
43+
return False

0 commit comments

Comments
 (0)