Skip to content

Commit d7a28dd

Browse files
authored
Enforce a blank line after a nested class in stubs (#3564)
1 parent a3e8247 commit d7a28dd

File tree

5 files changed

+34
-3
lines changed

5 files changed

+34
-3
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
compared to their non-async version. (#3609)
2424
- `with` statements that contain two context managers will be consistently wrapped in
2525
parentheses (#3589)
26+
- For stubs, enforce one blank line after a nested class with a body other than just
27+
`...` (#3564)
2628

2729
### Configuration
2830

src/black/lines.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ class EmptyLineTracker:
521521
mode: Mode
522522
previous_line: Optional[Line] = None
523523
previous_block: Optional[LinesBlock] = None
524-
previous_defs: List[int] = field(default_factory=list)
524+
previous_defs: List[Line] = field(default_factory=list)
525525
semantic_leading_comment: Optional[LinesBlock] = None
526526

527527
def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
@@ -577,12 +577,18 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
577577
else:
578578
before = 0
579579
depth = current_line.depth
580-
while self.previous_defs and self.previous_defs[-1] >= depth:
580+
while self.previous_defs and self.previous_defs[-1].depth >= depth:
581581
if self.mode.is_pyi:
582582
assert self.previous_line is not None
583583
if depth and not current_line.is_def and self.previous_line.is_def:
584584
# Empty lines between attributes and methods should be preserved.
585585
before = min(1, before)
586+
elif (
587+
Preview.blank_line_after_nested_stub_class in self.mode
588+
and self.previous_defs[-1].is_class
589+
and not self.previous_defs[-1].is_stub_class
590+
):
591+
before = 1
586592
elif depth:
587593
before = 0
588594
else:
@@ -637,7 +643,7 @@ def _maybe_empty_lines_for_class_or_def(
637643
self, current_line: Line, before: int
638644
) -> Tuple[int, int]:
639645
if not current_line.is_decorator:
640-
self.previous_defs.append(current_line.depth)
646+
self.previous_defs.append(current_line)
641647
if self.previous_line is None:
642648
# Don't insert empty lines before the first line in the file.
643649
return 0, 0

src/black/mode.py

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ class Preview(Enum):
154154
"""Individual preview style features."""
155155

156156
add_trailing_comma_consistently = auto()
157+
blank_line_after_nested_stub_class = auto()
157158
hex_codes_in_unicode_sequences = auto()
158159
improved_async_statements_handling = auto()
159160
multiline_string_handling = auto()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Outer:
2+
class InnerStub: ...
3+
outer_attr_after_inner_stub: int
4+
class Inner:
5+
inner_attr: int
6+
outer_attr: int
7+
8+
# output
9+
class Outer:
10+
class InnerStub: ...
11+
outer_attr_after_inner_stub: int
12+
13+
class Inner:
14+
inner_attr: int
15+
16+
outer_attr: int

tests/test_format.py

+6
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ def test_stub() -> None:
186186
assert_format(source, expected, mode)
187187

188188

189+
def test_nested_class_stub() -> None:
190+
mode = replace(DEFAULT_MODE, is_pyi=True, preview=True)
191+
source, expected = read_data("miscellaneous", "nested_class_stub.pyi")
192+
assert_format(source, expected, mode)
193+
194+
189195
def test_power_op_newline() -> None:
190196
# requires line_length=0
191197
source, expected = read_data("miscellaneous", "power_op_newline")

0 commit comments

Comments
 (0)