Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not add an extra blank line to an import line that has fmt disabled #3610

Merged
merged 3 commits into from Mar 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -10,6 +10,9 @@

<!-- Changes that affect Black's stable style -->

- 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

<!-- Changes that affect Black's preview style -->
Expand Down
1 change: 1 addition & 0 deletions src/black/comments.py
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions src/black/lines.py
Expand Up @@ -195,6 +195,26 @@ def opens_block(self) -> bool:
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."""
for leaf in self.leaves:
Expand Down Expand Up @@ -597,6 +617,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
Expand Down
6 changes: 6 additions & 0 deletions src/blib2to3/pytree.py
Expand Up @@ -392,6 +392,10 @@ 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 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__(
self,
Expand All @@ -401,6 +405,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.
Expand All @@ -419,6 +424,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."""
Expand Down
1 change: 0 additions & 1 deletion tests/data/simple_cases/fmtonoff.py
Expand Up @@ -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)
Expand Down
19 changes: 19 additions & 0 deletions 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