From ad564ad538c7accfb4934479af08fac74722f532 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Thu, 16 Mar 2023 17:56:35 -0700 Subject: [PATCH 1/3] Do not add an extra blank line to an import line that ends with # fmt: skip. --- src/black/comments.py | 1 + src/black/lines.py | 14 ++++++++++++++ src/blib2to3/pytree.py | 5 +++++ tests/data/simple_cases/fmtonoff.py | 1 - tests/data/simple_cases/fmtpass_imports.py | 19 +++++++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/data/simple_cases/fmtpass_imports.py diff --git a/src/black/comments.py b/src/black/comments.py index 7cf15bf67b3..619123ab4be 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -203,6 +203,7 @@ def convert_one_fmt_off_pair(node: Node) -> bool: STANDALONE_COMMENT, hidden_value, prefix=standalone_comment_prefix, + fmt_pass_converted_first_leaf=first_leaf_of(first), ), ) return True diff --git a/src/black/lines.py b/src/black/lines.py index 329dfc4f0d3..045f2ff4306 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -194,6 +194,19 @@ def opens_block(self) -> bool: if len(self.leaves) == 0: return False return self.leaves[-1].type == token.COLON + + def is_fmt_pass_converted(self, *, first_leaf_matches: Optional[Callable[[Leaf], bool]] = None) -> bool: + """Is this line converted from fmt off/skip code? + + If first_leaf_matches is not None, it only returns True if the first + leaf of converted code matches. + """ + if len(self.leaves) != 1: + return False + leaf = self.leaves[0] + if leaf.type != STANDALONE_COMMENT or leaf.fmt_pass_converted_first_leaf is None: + return False + return first_leaf_matches is None or first_leaf_matches(leaf.fmt_pass_converted_first_leaf) def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: """If so, needs to be split before emitting.""" @@ -597,6 +610,7 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: self.previous_line and self.previous_line.is_import and not current_line.is_import + and not current_line.is_fmt_pass_converted(first_leaf_matches=is_import) and depth == self.previous_line.depth ): return (before or 1), 0 diff --git a/src/blib2to3/pytree.py b/src/blib2to3/pytree.py index 15a1420ef7d..50d10d02e11 100644 --- a/src/blib2to3/pytree.py +++ b/src/blib2to3/pytree.py @@ -392,6 +392,9 @@ class Leaf(Base): _prefix = "" # Whitespace and comments preceding this token in the input lineno: int = 0 # Line where this token starts in the input column: int = 0 # Column where this token starts in the input + # If not None, this Leaf is created by converting block of fmt off/skip + # code, and it points to the first Leaf in the converted code. + fmt_pass_converted_first_leaf: Optional["Leaf"] = None def __init__( self, @@ -401,6 +404,7 @@ def __init__( prefix: Optional[Text] = None, fixers_applied: List[Any] = [], opening_bracket: Optional["Leaf"] = None, + fmt_pass_converted_first_leaf: Optional["Leaf"] = None, ) -> None: """ Initializer. @@ -419,6 +423,7 @@ def __init__( self.fixers_applied: Optional[List[Any]] = fixers_applied[:] self.children = [] self.opening_bracket = opening_bracket + self.fmt_pass_converted_first_leaf = fmt_pass_converted_first_leaf def __repr__(self) -> str: """Return a canonical string representation.""" diff --git a/tests/data/simple_cases/fmtonoff.py b/tests/data/simple_cases/fmtonoff.py index e40ea2c8d21..d1f15cd5c8b 100644 --- a/tests/data/simple_cases/fmtonoff.py +++ b/tests/data/simple_cases/fmtonoff.py @@ -195,7 +195,6 @@ def single_literal_yapf_disable(): from third_party import X, Y, Z from library import some_connection, some_decorator - # fmt: off from third_party import (X, Y, Z) diff --git a/tests/data/simple_cases/fmtpass_imports.py b/tests/data/simple_cases/fmtpass_imports.py new file mode 100644 index 00000000000..8b3c0bc662a --- /dev/null +++ b/tests/data/simple_cases/fmtpass_imports.py @@ -0,0 +1,19 @@ +# Regression test for https://github.com/psf/black/issues/3438 + +import ast +import collections # fmt: skip +import dataclasses +# fmt: off +import os +# fmt: on +import pathlib + +import re # fmt: skip +import secrets + +# fmt: off +import sys +# fmt: on + +import tempfile +import zoneinfo From a86ae09f60feca601434141cb0c5b72b9893aa3e Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Thu, 16 Mar 2023 19:04:06 -0700 Subject: [PATCH 2/3] Format and add changelog. --- CHANGES.md | 3 +++ src/black/lines.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index eff2640a01e..06a0ab7e9eb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,9 @@ +- Import lines with `# fmt: skip` and `# fmt: off` no longer have an extra blank line + added when they are right after another import line (#3610) + ### Preview style diff --git a/src/black/lines.py b/src/black/lines.py index 045f2ff4306..66bba14b357 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -194,19 +194,26 @@ def opens_block(self) -> bool: if len(self.leaves) == 0: return False return self.leaves[-1].type == token.COLON - - def is_fmt_pass_converted(self, *, first_leaf_matches: Optional[Callable[[Leaf], bool]] = None) -> bool: + + def is_fmt_pass_converted( + self, *, first_leaf_matches: Optional[Callable[[Leaf], bool]] = None + ) -> bool: """Is this line converted from fmt off/skip code? - + If first_leaf_matches is not None, it only returns True if the first leaf of converted code matches. """ if len(self.leaves) != 1: return False leaf = self.leaves[0] - if leaf.type != STANDALONE_COMMENT or leaf.fmt_pass_converted_first_leaf is None: + if ( + leaf.type != STANDALONE_COMMENT + or leaf.fmt_pass_converted_first_leaf is None + ): return False - return first_leaf_matches is None or first_leaf_matches(leaf.fmt_pass_converted_first_leaf) + return first_leaf_matches is None or first_leaf_matches( + leaf.fmt_pass_converted_first_leaf + ) def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: """If so, needs to be split before emitting.""" From c7fa45216625a3504f35eb7937766b876f0ecba4 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Thu, 16 Mar 2023 19:34:13 -0700 Subject: [PATCH 3/3] Clarify the comment. --- src/blib2to3/pytree.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/blib2to3/pytree.py b/src/blib2to3/pytree.py index 50d10d02e11..ea60c894e20 100644 --- a/src/blib2to3/pytree.py +++ b/src/blib2to3/pytree.py @@ -392,8 +392,9 @@ class Leaf(Base): _prefix = "" # Whitespace and comments preceding this token in the input lineno: int = 0 # Line where this token starts in the input column: int = 0 # Column where this token starts in the input - # If not None, this Leaf is created by converting block of fmt off/skip - # code, and it points to the first Leaf in the converted code. + # If not None, this Leaf is created by converting a block of fmt off/skip + # code, and `fmt_pass_converted_first_leaf` points to the first Leaf in the + # converted code. fmt_pass_converted_first_leaf: Optional["Leaf"] = None def __init__(