From e54f86bae497d45405e113a073b194e3046f4a36 Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Tue, 3 Dec 2024 23:39:35 -0500 Subject: [PATCH] Two blank lines after an import should be reduced to one (#4489) Co-authored-by: Jelle Zijlstra --- CHANGES.md | 1 + docs/the_black_code_style/future_style.md | 2 + src/black/lines.py | 9 + src/black/mode.py | 1 + src/black/resources/black.schema.json | 3 +- tests/data/cases/preview_comments7.py | 1 - .../cases/preview_import_line_collapse.py | 180 ++++++++++++++++++ 7 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 tests/data/cases/preview_import_line_collapse.py diff --git a/CHANGES.md b/CHANGES.md index 8f374f30dd7..505fee4d743 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,7 @@ - Remove parentheses around sole list items (#4312) +- Collapse multiple empty lines after an import into one (#4489) ### Configuration diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index 0b2e5f8b02c..3fc12e2dab6 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -41,6 +41,8 @@ Currently, the following features are included in the preview style: - `remove_lone_list_item_parens`: remove redundant parentheses around lone list items (depends on unstable `hug_parens_with_braces_and_square_brackets` feature in some cases) +- `always_one_newline_after_import`: Always force one blank line after import + statements, except when the line after the import is a comment or an import statement (labels/unstable-features)= diff --git a/src/black/lines.py b/src/black/lines.py index c488ea77712..dd91bf898f2 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -671,6 +671,15 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: # noqa: C9 current_line, before, user_had_newline ) + if ( + self.previous_line.is_import + and self.previous_line.depth == 0 + and current_line.depth == 0 + and not current_line.is_import + and Preview.always_one_newline_after_import in self.mode + ): + return 1, 0 + if ( self.previous_line.is_import and not current_line.is_import diff --git a/src/black/mode.py b/src/black/mode.py index 8ab71bce660..96f72cc2ded 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -214,6 +214,7 @@ class Preview(Enum): # hug_parens_with_braces_and_square_brackets to remove parens in some cases remove_lone_list_item_parens = auto() pep646_typed_star_arg_type_var_tuple = auto() + always_one_newline_after_import = auto() UNSTABLE_FEATURES: set[Preview] = { diff --git a/src/black/resources/black.schema.json b/src/black/resources/black.schema.json index 521468da2a5..f1d471e7616 100644 --- a/src/black/resources/black.schema.json +++ b/src/black/resources/black.schema.json @@ -92,7 +92,8 @@ "remove_redundant_guard_parens", "parens_for_long_if_clauses_in_case_block", "remove_lone_list_item_parens", - "pep646_typed_star_arg_type_var_tuple" + "pep646_typed_star_arg_type_var_tuple", + "always_one_newline_after_import" ] }, "description": "Enable specific features included in the `--unstable` style. Requires `--preview`. No compatibility guarantees are provided on the behavior or existence of any unstable features." diff --git a/tests/data/cases/preview_comments7.py b/tests/data/cases/preview_comments7.py index e4d547138db..703e3c8fbde 100644 --- a/tests/data/cases/preview_comments7.py +++ b/tests/data/cases/preview_comments7.py @@ -177,7 +177,6 @@ def test_fails_invalid_post_data( MyLovelyCompanyTeamProjectComponent as component, # DRY ) - result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/tests/data/cases/preview_import_line_collapse.py b/tests/data/cases/preview_import_line_collapse.py new file mode 100644 index 00000000000..74ae349a2ca --- /dev/null +++ b/tests/data/cases/preview_import_line_collapse.py @@ -0,0 +1,180 @@ +# flags: --preview +from middleman.authentication import validate_oauth_token + + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token +#comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + + + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + + + +try: + import os +except Exception: + pass + +try: + import os + def func(): + a = 1 +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + + + + + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + def func(): + pass + print() + + +def func(): + import os + a = 1 + print() + + +def func(): + import os + + + a = 1 + print() + + +def func(): + import os + + + + a = 1 + print() + +# output + + +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token + +# comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token + +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + +try: + import os +except Exception: + pass + +try: + import os + + def func(): + a = 1 + +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + + def func(): + pass + + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print()