Skip to content

Commit

Permalink
Blank line between nested and function def in stub files. (#3862)
Browse files Browse the repository at this point in the history
The idea behind this change is that we stop looking into previous body to determine if there should be a blank before a function or class definition.

Input:

```python
import sys

if sys.version_info > (3, 7):
    class Nested1:
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    class Nested2:
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...

if sys.version_info > (3, 7):
    def nested1():
        assignment = 1
        def function_definition(self): ...
    def f1(self) -> str: ...
    def nested2():
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
```

Stable style
```python
import sys

if sys.version_info > (3, 7):
    class Nested1:
        assignment = 1
        def function_definition(self): ...

    def f1(self) -> str: ...

    class Nested2:
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...

if sys.version_info > (3, 7):
    def nested1():
        assignment = 1
        def function_definition(self): ...

    def f1(self) -> str: ...
    def nested2():
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
```

In the stable formatting, we have a blank line sometimes, not depending on the previous statement on the same level, but on the last (potentially nested) statement in the previous body.

#2783/#3564 fixes this for classes in preview style:

```python
import sys

if sys.version_info > (3, 7):
    class Nested1:
        assignment = 1
        def function_definition(self): ...

    def f1(self) -> str: ...

    class Nested2:
        def function_definition(self): ...
        assignment = 1

    def f2(self) -> str: ...

if sys.version_info > (3, 7):
    def nested1():
        assignment = 1
        def function_definition(self): ...

    def f1(self) -> str: ...
    def nested2():
        def function_definition(self): ...
        assignment = 1
    def f2(self) -> str: ...
```

This PR additionally fixes this for function definitions:

```python
if sys.version_info > (3, 7):
    if sys.platform == "win32":
        assignment = 1
        def function_definition(self): ...

    def f1(self) -> str: ...
    if sys.platform != "win32":
        def function_definition(self): ...
        assignment = 1

    def f2(self) -> str: ...

if sys.version_info > (3, 8):
    if sys.platform == "win32":
        assignment = 1
        def function_definition(self): ...

    class F1: ...
    if sys.platform != "win32":
        def function_definition(self): ...
        assignment = 1

    class F2: ...
```

You can see the effect of this change on typeshed in https://github.com/konstin/typeshed/pull/1/files. As baseline, the preview mode changes without this PR are at konstin/typeshed#2.

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
  • Loading branch information
konstin and JelleZijlstra committed Sep 9, 2023
1 parent a20338c commit b40b01f
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 18 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Expand Up @@ -171,6 +171,8 @@ expected to become part of Black's stable style in January 2024.
- For stubs, enforce one blank line after a nested class with a body other than just
`...` (#3564)
- Improve handling of multiline strings by changing line split behavior (#1879)
- In stub files, add a blank line between a statement with a body (e.g an
`if sys.version_info > (3, x):`) and a function definition on the same level. (#3862)

### Parser

Expand Down
11 changes: 11 additions & 0 deletions src/black/lines.py
Expand Up @@ -711,6 +711,17 @@ def _maybe_empty_lines_for_class_or_def( # noqa: C901
newlines = 0
else:
newlines = 1
# Remove case `self.previous_line.depth > current_line.depth` below when
# this becomes stable.
#
# Don't inspect the previous line if it's part of the body of the previous
# statement in the same level, we always want a blank line if there's
# something with a body preceding.
elif (
Preview.blank_line_between_nested_and_def_stub_file in current_line.mode
and self.previous_line.depth > current_line.depth
):
newlines = 1
elif (
current_line.is_def or current_line.is_decorator
) and not self.previous_line.is_def:
Expand Down
1 change: 1 addition & 0 deletions src/black/mode.py
Expand Up @@ -170,6 +170,7 @@ class Preview(Enum):

add_trailing_comma_consistently = auto()
blank_line_after_nested_stub_class = auto()
blank_line_between_nested_and_def_stub_file = auto()
hex_codes_in_unicode_sequences = auto()
improved_async_statements_handling = auto()
multiline_string_handling = auto()
Expand Down
16 changes: 0 additions & 16 deletions tests/data/miscellaneous/nested_class_stub.pyi

This file was deleted.

43 changes: 43 additions & 0 deletions tests/data/miscellaneous/nested_stub.pyi
@@ -0,0 +1,43 @@
import sys

class Outer:
class InnerStub: ...
outer_attr_after_inner_stub: int
class Inner:
inner_attr: int
outer_attr: int

if sys.version_info > (3, 7):
if sys.platform == "win32":
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
if sys.platform != "win32":
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...

# output

import sys

class Outer:
class InnerStub: ...
outer_attr_after_inner_stub: int

class Inner:
inner_attr: int

outer_attr: int

if sys.version_info > (3, 7):
if sys.platform == "win32":
assignment = 1
def function_definition(self): ...

def f1(self) -> str: ...
if sys.platform != "win32":
def function_definition(self): ...
assignment = 1

def f2(self) -> str: ...
4 changes: 2 additions & 2 deletions tests/test_format.py
Expand Up @@ -201,9 +201,9 @@ def test_stub() -> None:
assert_format(source, expected, mode)


def test_nested_class_stub() -> None:
def test_nested_stub() -> None:
mode = replace(DEFAULT_MODE, is_pyi=True, preview=True)
source, expected = read_data("miscellaneous", "nested_class_stub.pyi")
source, expected = read_data("miscellaneous", "nested_stub.pyi")
assert_format(source, expected, mode)


Expand Down

0 comments on commit b40b01f

Please sign in to comment.