From 40f47c86f6d295351e8ace0349347d4cb67a6c29 Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Wed, 16 Oct 2024 19:05:07 -0400 Subject: [PATCH 1/8] Collapse multiple lines after import into single line --- CHANGES.md | 2 ++ src/black/lines.py | 11 ++++++++++- tests/data/cases/import_line_collapse.py | 12 ++++++++++++ tests/data/cases/import_line_collapse_3_to_1.py | 13 +++++++++++++ tests/data/cases/preview_comments7.py | 1 - 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/data/cases/import_line_collapse.py create mode 100644 tests/data/cases/import_line_collapse_3_to_1.py diff --git a/CHANGES.md b/CHANGES.md index d50bcb49e00..f907e86bc0c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ +- Collapse multiple lines into 1 after import (#4488) + ### Preview style diff --git a/src/black/lines.py b/src/black/lines.py index a8c6ef66f68..a7a2ceb1dba 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -605,7 +605,16 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: # noqa: C9 # Consume the first leaf's extra newlines. first_leaf = current_line.leaves[0] before = first_leaf.prefix.count("\n") - before = min(before, max_allowed) + before = ( + 1 + if self.previous_line + and self.previous_line.is_import + and self.previous_line.depth == 0 + and current_line.depth == 0 + and not current_line.is_import + and not current_line.is_comment + else min(before, max_allowed) + ) first_leaf.prefix = "" else: before = 0 diff --git a/tests/data/cases/import_line_collapse.py b/tests/data/cases/import_line_collapse.py new file mode 100644 index 00000000000..b5029fc2721 --- /dev/null +++ b/tests/data/cases/import_line_collapse.py @@ -0,0 +1,12 @@ +from middleman.authentication import validate_oauth_token + + +logger = logging.getLogger(__name__) + + +# output + + +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) diff --git a/tests/data/cases/import_line_collapse_3_to_1.py b/tests/data/cases/import_line_collapse_3_to_1.py new file mode 100644 index 00000000000..65b2ed7d322 --- /dev/null +++ b/tests/data/cases/import_line_collapse_3_to_1.py @@ -0,0 +1,13 @@ +from middleman.authentication import validate_oauth_token + + + +logger = logging.getLogger(__name__) + + +# output + + +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) 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 From 332db9bc0c0358989a6b2eb22f52a7bf9c496f15 Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Wed, 16 Oct 2024 19:15:24 -0400 Subject: [PATCH 2/8] fix PR number --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e9e0ca15460..19f1d25fca2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,7 +10,7 @@ -- Collapse multiple lines into 1 after import (#4488) +- Collapse multiple lines into 1 after import (#4489) - Fix formatting cells in IPython notebooks with magic methods and starting or trailing empty lines (#4484) From a8a3f8abe2127f6f0f1ccd1937a7fa4ad4d110ef Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Wed, 16 Oct 2024 22:43:19 -0400 Subject: [PATCH 3/8] addressing comments --- CHANGES.md | 3 ++- docs/the_black_code_style/future_style.md | 2 ++ src/black/lines.py | 1 + src/black/mode.py | 1 + src/black/resources/black.schema.json | 3 ++- ...import_line_collapse.py => preview_import_line_collapse.py} | 1 + ...llapse_3_to_1.py => preview_import_line_collapse_3_to_1.py} | 1 + 7 files changed, 10 insertions(+), 2 deletions(-) rename tests/data/cases/{import_line_collapse.py => preview_import_line_collapse.py} (91%) rename tests/data/cases/{import_line_collapse_3_to_1.py => preview_import_line_collapse_3_to_1.py} (91%) diff --git a/CHANGES.md b/CHANGES.md index 19f1d25fca2..3c427cacfcf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,7 +10,6 @@ -- Collapse multiple lines into 1 after import (#4489) - Fix formatting cells in IPython notebooks with magic methods and starting or trailing empty lines (#4484) @@ -18,6 +17,8 @@ +- Collapse multiple lines into 1 after import (#4489) + ### Configuration diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index cd4fb12bd51..f0047e5a109 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -38,6 +38,8 @@ Currently, the following features are included in the preview style: blocks when the line is too long - `pep646_typed_star_arg_type_var_tuple`: fix type annotation spacing between * and more complex type variable tuple (i.e. `def fn(*args: *tuple[*Ts, T]) -> None: pass`) +- `always_one_newline_after_import`: Always force one newline after import statements + except for 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 a7a2ceb1dba..8d6c87e6d2e 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -613,6 +613,7 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: # noqa: C9 and current_line.depth == 0 and not current_line.is_import and not current_line.is_comment + and Preview.always_one_newline_after_import in self.mode else min(before, max_allowed) ) first_leaf.prefix = "" diff --git a/src/black/mode.py b/src/black/mode.py index 02fe1de24db..171a57cbb0d 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -211,6 +211,7 @@ class Preview(Enum): remove_redundant_guard_parens = auto() parens_for_long_if_clauses_in_case_block = 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 a536d543fed..fba2f8d465b 100644 --- a/src/black/resources/black.schema.json +++ b/src/black/resources/black.schema.json @@ -91,7 +91,8 @@ "docstring_check_for_newline", "remove_redundant_guard_parens", "parens_for_long_if_clauses_in_case_block", - "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/import_line_collapse.py b/tests/data/cases/preview_import_line_collapse.py similarity index 91% rename from tests/data/cases/import_line_collapse.py rename to tests/data/cases/preview_import_line_collapse.py index b5029fc2721..edfa60a4b15 100644 --- a/tests/data/cases/import_line_collapse.py +++ b/tests/data/cases/preview_import_line_collapse.py @@ -1,3 +1,4 @@ +# flags: --preview from middleman.authentication import validate_oauth_token diff --git a/tests/data/cases/import_line_collapse_3_to_1.py b/tests/data/cases/preview_import_line_collapse_3_to_1.py similarity index 91% rename from tests/data/cases/import_line_collapse_3_to_1.py rename to tests/data/cases/preview_import_line_collapse_3_to_1.py index 65b2ed7d322..0bfda3781ab 100644 --- a/tests/data/cases/import_line_collapse_3_to_1.py +++ b/tests/data/cases/preview_import_line_collapse_3_to_1.py @@ -1,3 +1,4 @@ +# flags: --preview from middleman.authentication import validate_oauth_token From 8e923a572b8a3040d423a635bb6ccbb651d48c85 Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Fri, 18 Oct 2024 19:40:28 -0400 Subject: [PATCH 4/8] add more test cases, modify logic: --- src/black/lines.py | 1 - .../cases/preview_import_line_collapse.py | 28 +++++++++++ .../preview_import_line_collapse_3_to_1.py | 48 +++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/black/lines.py b/src/black/lines.py index 8d6c87e6d2e..f5386196a98 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -612,7 +612,6 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: # noqa: C9 and self.previous_line.depth == 0 and current_line.depth == 0 and not current_line.is_import - and not current_line.is_comment and Preview.always_one_newline_after_import in self.mode else min(before, max_allowed) ) diff --git a/tests/data/cases/preview_import_line_collapse.py b/tests/data/cases/preview_import_line_collapse.py index edfa60a4b15..8f45f1cabdb 100644 --- a/tests/data/cases/preview_import_line_collapse.py +++ b/tests/data/cases/preview_import_line_collapse.py @@ -5,9 +5,37 @@ 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__) + + # 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__) diff --git a/tests/data/cases/preview_import_line_collapse_3_to_1.py b/tests/data/cases/preview_import_line_collapse_3_to_1.py index 0bfda3781ab..02b351c1000 100644 --- a/tests/data/cases/preview_import_line_collapse_3_to_1.py +++ b/tests/data/cases/preview_import_line_collapse_3_to_1.py @@ -6,9 +6,57 @@ logger = logging.getLogger(__name__) +# case 2 try catch with import after import +import os +import os + + + +try: + import os +except Exception: + pass + + +# case 3 multiple imports +import os +import os + +import os +import os + + + + + +for i in range(10): + print(i) + + # output from middleman.authentication import validate_oauth_token logger = logging.getLogger(__name__) + + +# case 2 try catch with import after import +import os +import os + +try: + import os +except Exception: + pass + + +# case 3 multiple imports +import os +import os + +import os +import os + +for i in range(10): + print(i) From bc956918f7331f1f0495a1f84eaed6b8d7a37ae6 Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Thu, 14 Nov 2024 19:20:33 -0500 Subject: [PATCH 5/8] addressing comments --- src/black/lines.py | 20 ++-- .../cases/preview_import_line_collapse.py | 108 ++++++++++++++++++ .../preview_import_line_collapse_3_to_1.py | 62 ---------- 3 files changed, 118 insertions(+), 72 deletions(-) delete mode 100644 tests/data/cases/preview_import_line_collapse_3_to_1.py diff --git a/src/black/lines.py b/src/black/lines.py index 0256b305df9..dd91bf898f2 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -606,16 +606,7 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: # noqa: C9 # Consume the first leaf's extra newlines. first_leaf = current_line.leaves[0] before = first_leaf.prefix.count("\n") - before = ( - 1 - if self.previous_line - and 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 - else min(before, max_allowed) - ) + before = min(before, max_allowed) first_leaf.prefix = "" else: before = 0 @@ -680,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/tests/data/cases/preview_import_line_collapse.py b/tests/data/cases/preview_import_line_collapse.py index 8f45f1cabdb..39efd93d7e8 100644 --- a/tests/data/cases/preview_import_line_collapse.py +++ b/tests/data/cases/preview_import_line_collapse.py @@ -18,6 +18,61 @@ 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() + # output @@ -39,3 +94,56 @@ # 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() diff --git a/tests/data/cases/preview_import_line_collapse_3_to_1.py b/tests/data/cases/preview_import_line_collapse_3_to_1.py deleted file mode 100644 index 02b351c1000..00000000000 --- a/tests/data/cases/preview_import_line_collapse_3_to_1.py +++ /dev/null @@ -1,62 +0,0 @@ -# flags: --preview -from middleman.authentication import validate_oauth_token - - - -logger = logging.getLogger(__name__) - - -# case 2 try catch with import after import -import os -import os - - - -try: - import os -except Exception: - pass - - -# case 3 multiple imports -import os -import os - -import os -import os - - - - - -for i in range(10): - print(i) - - -# output - - -from middleman.authentication import validate_oauth_token - -logger = logging.getLogger(__name__) - - -# case 2 try catch with import after import -import os -import os - -try: - import os -except Exception: - pass - - -# case 3 multiple imports -import os -import os - -import os -import os - -for i in range(10): - print(i) From 7efcd9cb8282ff700f2a5022ae6046fcd071a3b4 Mon Sep 17 00:00:00 2001 From: Owen Christie Date: Thu, 14 Nov 2024 19:31:01 -0500 Subject: [PATCH 6/8] expand test case --- .../cases/preview_import_line_collapse.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/data/cases/preview_import_line_collapse.py b/tests/data/cases/preview_import_line_collapse.py index 39efd93d7e8..74ae349a2ca 100644 --- a/tests/data/cases/preview_import_line_collapse.py +++ b/tests/data/cases/preview_import_line_collapse.py @@ -73,6 +73,23 @@ def func(): a = 1 print() + +def func(): + import os + + + a = 1 + print() + + +def func(): + import os + + + + a = 1 + print() + # output @@ -147,3 +164,17 @@ def func(): a = 1 print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() From f85cd2d21c38667017e3b257d45bf8c7f392120a Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 3 Dec 2024 20:27:34 -0800 Subject: [PATCH 7/8] Apply suggestions from code review --- CHANGES.md | 2 +- docs/the_black_code_style/future_style.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e1a02ac5f63..505fee4d743 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,7 +18,7 @@ - Remove parentheses around sole list items (#4312) -- Collapse multiple lines into 1 after import (#4489) +- 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 d2cde41567e..c74817f5c31 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -41,8 +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 newline after import statements - except for when the line after the import is a comment or an import statement +- `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)= From ee7ea60189238523e3641a2dbe1d82007bf46efc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 04:27:59 +0000 Subject: [PATCH 8/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/the_black_code_style/future_style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index c74817f5c31..3fc12e2dab6 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -41,7 +41,7 @@ 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 +- `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)=