From 3086ece81b686343a2e657cc74c51b57b9d8072a Mon Sep 17 00:00:00 2001 From: WMOkiishi Date: Sat, 11 Feb 2023 15:19:05 -0700 Subject: [PATCH 1/3] Enforce a blank line after a nested class in stubs --- CHANGES.md | 3 +++ src/black/lines.py | 8 ++++---- tests/data/miscellaneous/stub.pyi | 11 +++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a8b556cb7e8..106a419402a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,9 @@ +- For stubs, enforce one blank line after a nested class with a body other than just + `...` (#3564) + ### Preview style diff --git a/src/black/lines.py b/src/black/lines.py index ec6ef5d9522..daff7d97f08 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -481,7 +481,7 @@ class EmptyLineTracker: mode: Mode previous_line: Optional[Line] = None previous_block: Optional[LinesBlock] = None - previous_defs: List[int] = field(default_factory=list) + previous_defs: List[Line] = field(default_factory=list) semantic_leading_comment: Optional[LinesBlock] = None def maybe_empty_lines(self, current_line: Line) -> LinesBlock: @@ -537,13 +537,13 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: else: before = 0 depth = current_line.depth - while self.previous_defs and self.previous_defs[-1] >= depth: + while self.previous_defs and self.previous_defs[-1].depth >= depth: if self.mode.is_pyi: assert self.previous_line is not None if depth and not current_line.is_def and self.previous_line.is_def: # Empty lines between attributes and methods should be preserved. before = min(1, before) - elif depth: + elif depth and not self.previous_defs[-1].is_class: before = 0 else: before = 1 @@ -596,7 +596,7 @@ def _maybe_empty_lines_for_class_or_def( self, current_line: Line, before: int ) -> Tuple[int, int]: if not current_line.is_decorator: - self.previous_defs.append(current_line.depth) + self.previous_defs.append(current_line) if self.previous_line is None: # Don't insert empty lines before the first line in the file. return 0, 0 diff --git a/tests/data/miscellaneous/stub.pyi b/tests/data/miscellaneous/stub.pyi index af2cd2c2c02..6bd23b40caf 100644 --- a/tests/data/miscellaneous/stub.pyi +++ b/tests/data/miscellaneous/stub.pyi @@ -56,6 +56,11 @@ class Nested: def who_has_to_know(self): ... def verse(self): ... +class Outer: + class Inner: + inner_attr: int + outer_attr: int + class Conditional: def f(self): ... if sys.version_info >= (3, 8): @@ -129,6 +134,12 @@ class Nested: def verse(self): ... +class Outer: + class Inner: + inner_attr: int + + outer_attr: int + class Conditional: def f(self): ... if sys.version_info >= (3, 8): From aace98809bcaf425766a863e9e3640bd89a62f13 Mon Sep 17 00:00:00 2001 From: WMOkiishi Date: Sun, 12 Feb 2023 10:31:33 -0700 Subject: [PATCH 2/3] Don't add a blank line after a class with no body --- src/black/lines.py | 7 ++++++- tests/data/miscellaneous/stub.pyi | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/black/lines.py b/src/black/lines.py index daff7d97f08..af785b70f3f 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -543,7 +543,12 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: if depth and not current_line.is_def and self.previous_line.is_def: # Empty lines between attributes and methods should be preserved. before = min(1, before) - elif depth and not self.previous_defs[-1].is_class: + elif ( + self.previous_defs[-1].is_class + and not self.previous_defs[-1].is_stub_class + ): + before = 1 + elif depth: before = 0 else: before = 1 diff --git a/tests/data/miscellaneous/stub.pyi b/tests/data/miscellaneous/stub.pyi index 6bd23b40caf..d621e556854 100644 --- a/tests/data/miscellaneous/stub.pyi +++ b/tests/data/miscellaneous/stub.pyi @@ -57,6 +57,8 @@ class Nested: def verse(self): ... class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int class Inner: inner_attr: int outer_attr: int @@ -135,6 +137,9 @@ class Nested: def verse(self): ... class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + class Inner: inner_attr: int From faed181159dbe6cc9fecd4e769332583941e6d3f Mon Sep 17 00:00:00 2001 From: WMOkiishi Date: Sun, 12 Feb 2023 11:01:44 -0700 Subject: [PATCH 3/3] Move changes to the preview style --- CHANGES.md | 5 ++--- src/black/lines.py | 5 +++-- src/black/mode.py | 1 + tests/data/miscellaneous/nested_class_stub.pyi | 16 ++++++++++++++++ tests/data/miscellaneous/stub.pyi | 16 ---------------- tests/test_format.py | 6 ++++++ 6 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 tests/data/miscellaneous/nested_class_stub.pyi diff --git a/CHANGES.md b/CHANGES.md index 106a419402a..771c125e58b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,15 +10,14 @@ -- For stubs, enforce one blank line after a nested class with a body other than just - `...` (#3564) - ### Preview style - Add trailing commas to collection literals even if there's a comment after the last entry (#3393) +- For stubs, enforce one blank line after a nested class with a body other than just + `...` (#3564) ### Configuration diff --git a/src/black/lines.py b/src/black/lines.py index af785b70f3f..119bc8b6ce6 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -14,7 +14,7 @@ ) from black.brackets import DOT_PRIORITY, BracketTracker -from black.mode import Mode +from black.mode import Mode, Preview from black.nodes import ( BRACKETS, CLOSING_BRACKETS, @@ -544,7 +544,8 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: # Empty lines between attributes and methods should be preserved. before = min(1, before) elif ( - self.previous_defs[-1].is_class + Preview.blank_line_after_nested_stub_class in self.mode + and self.previous_defs[-1].is_class and not self.previous_defs[-1].is_stub_class ): before = 1 diff --git a/src/black/mode.py b/src/black/mode.py index c9a4c2b080c..9828424ac67 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -154,6 +154,7 @@ class Preview(Enum): """Individual preview style features.""" add_trailing_comma_consistently = auto() + blank_line_after_nested_stub_class = auto() hex_codes_in_unicode_sequences = auto() prefer_splitting_right_hand_side_of_assignments = auto() # NOTE: string_processing requires wrap_long_dict_values_in_parens diff --git a/tests/data/miscellaneous/nested_class_stub.pyi b/tests/data/miscellaneous/nested_class_stub.pyi new file mode 100644 index 00000000000..daf281b517b --- /dev/null +++ b/tests/data/miscellaneous/nested_class_stub.pyi @@ -0,0 +1,16 @@ +class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + class Inner: + inner_attr: int + outer_attr: int + +# output +class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + + class Inner: + inner_attr: int + + outer_attr: int diff --git a/tests/data/miscellaneous/stub.pyi b/tests/data/miscellaneous/stub.pyi index d621e556854..af2cd2c2c02 100644 --- a/tests/data/miscellaneous/stub.pyi +++ b/tests/data/miscellaneous/stub.pyi @@ -56,13 +56,6 @@ class Nested: def who_has_to_know(self): ... def verse(self): ... -class Outer: - class InnerStub: ... - outer_attr_after_inner_stub: int - class Inner: - inner_attr: int - outer_attr: int - class Conditional: def f(self): ... if sys.version_info >= (3, 8): @@ -136,15 +129,6 @@ class Nested: def verse(self): ... -class Outer: - class InnerStub: ... - outer_attr_after_inner_stub: int - - class Inner: - inner_attr: int - - outer_attr: int - class Conditional: def f(self): ... if sys.version_info >= (3, 8): diff --git a/tests/test_format.py b/tests/test_format.py index ab849aac9a3..3a6cbc9e2e9 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -186,6 +186,12 @@ def test_stub() -> None: assert_format(source, expected, mode) +def test_nested_class_stub() -> None: + mode = replace(DEFAULT_MODE, is_pyi=True, preview=True) + source, expected = read_data("miscellaneous", "nested_class_stub.pyi") + assert_format(source, expected, mode) + + def test_power_op_newline() -> None: # requires line_length=0 source, expected = read_data("miscellaneous", "power_op_newline")