From d0f3f2d8b3857b62270e9628c148223e7bf632ee Mon Sep 17 00:00:00 2001 From: Egorka1988 Date: Tue, 25 Jun 2019 19:57:51 +0300 Subject: [PATCH] fix issue #251: isort removes newlines before standalone comments if the following line is an import of the same group --- .gitignore | 1 + black.py | 18 +++- tests/data/import_handling_issue_251.py | 112 ++++++++++++++++++++++++ tests/test_black.py | 8 ++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 tests/data/import_handling_issue_251.py diff --git a/.gitignore b/.gitignore index 73cc19ad95e..daf08e9e5bc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ _build .vscode docs/_static/pypi.svg .tox +.idea __pycache__ black.egg-info build/ diff --git a/black.py b/black.py index ce27653af14..6626785872f 100644 --- a/black.py +++ b/black.py @@ -53,6 +53,7 @@ __version__ = "19.3b0" DEFAULT_LINE_LENGTH = 88 +DEFAULT_IMPORT_MODE = 0 DEFAULT_EXCLUDES = ( r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/" ) @@ -182,6 +183,7 @@ class Feature(Enum): @dataclass class FileMode: target_versions: Set[TargetVersion] = Factory(set) + import_mode: int = DEFAULT_IMPORT_MODE line_length: int = DEFAULT_LINE_LENGTH string_normalization: bool = True is_pyi: bool = False @@ -359,6 +361,14 @@ def read_pyproject_toml( ), is_eager=True, ) +@click.option( + "--import-mode", + default=DEFAULT_IMPORT_MODE, + help=( + "0-handle imports as usual. Default value\n" + "1-skip adding empty line between standalone comment and import\n" + ), +) @click.option( "--config", type=click.Path( @@ -370,6 +380,7 @@ def read_pyproject_toml( ) @click.pass_context def main( + import_mode: int, ctx: click.Context, code: Optional[str], line_length: int, @@ -406,6 +417,7 @@ def main( versions = set() mode = FileMode( target_versions=versions, + import_mode=import_mode, line_length=line_length, is_pyi=pyi, string_normalization=not skip_string_normalization, @@ -721,7 +733,7 @@ def format_str(src_contents: str, *, mode: FileMode) -> FileContent: is_pyi=mode.is_pyi, normalize_strings=mode.string_normalization, ) - elt = EmptyLineTracker(is_pyi=mode.is_pyi) + elt = EmptyLineTracker(is_pyi=mode.is_pyi, import_mode=mode.import_mode) empty_line = Line() after = 0 split_line_features = { @@ -1464,6 +1476,7 @@ class EmptyLineTracker: are consumed by `maybe_empty_lines()` and included in the computation. """ + import_mode: int = DEFAULT_IMPORT_MODE is_pyi: bool = False previous_line: Optional[Line] = None previous_after: int = 0 @@ -1504,7 +1517,8 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: return self._maybe_empty_lines_for_class_or_def(current_line, before) if ( - self.previous_line + self.import_mode == 0 + and self.previous_line and self.previous_line.is_import and not current_line.is_import and depth == self.previous_line.depth diff --git a/tests/data/import_handling_issue_251.py b/tests/data/import_handling_issue_251.py new file mode 100644 index 00000000000..614155ce21b --- /dev/null +++ b/tests/data/import_handling_issue_251.py @@ -0,0 +1,112 @@ +"""The asyncio package, tracking PEP 3156.""" + +# flake8: noqa + +from logging import ( + ERROR, +) +import sys + +# This relies on each of the submodules having an __all__ variable. +from .base_events import * +from .coroutines import * +from .events import * # comment here +# bugfix_1 +from .futures import * +from .locks import * # comment here +from .protocols import * +# bugfix_2 +from ..runners import * # comment here +from ..queues import * +from ..streams import * + +from some_library import ( + Just, Enough, Libraries, To, Fit, In, This, Nice, Split, Which, We, No, Longer, Use +) +from name_of_a_company.extremely_long_project_name.component.ttypes import CuteLittleServiceHandlerFactoryyy +from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * + +from .a.b.c.subprocess import * +from . import (tasks) +from . import (A, B, C) +from . import SomeVeryLongNameAndAllOfItsAdditionalLetters1, \ + SomeVeryLongNameAndAllOfItsAdditionalLetters2 +# bugfix_3 +__all__ = ( + base_events.__all__ + + coroutines.__all__ + + events.__all__ + + futures.__all__ + + locks.__all__ + + protocols.__all__ + + runners.__all__ + + queues.__all__ + + streams.__all__ + + tasks.__all__ +) + + +# output + + +"""The asyncio package, tracking PEP 3156.""" + +# flake8: noqa + +from logging import ERROR +import sys + +# This relies on each of the submodules having an __all__ variable. +from .base_events import * +from .coroutines import * +from .events import * # comment here +# bugfix_1 +from .futures import * +from .locks import * # comment here +from .protocols import * +# bugfix_2 +from ..runners import * # comment here +from ..queues import * +from ..streams import * + +from some_library import ( + Just, + Enough, + Libraries, + To, + Fit, + In, + This, + Nice, + Split, + Which, + We, + No, + Longer, + Use, +) +from name_of_a_company.extremely_long_project_name.component.ttypes import ( + CuteLittleServiceHandlerFactoryyy, +) +from name_of_a_company.extremely_long_project_name.extremely_long_component_name.ttypes import * + +from .a.b.c.subprocess import * +from . import tasks +from . import A, B, C +from . import ( + SomeVeryLongNameAndAllOfItsAdditionalLetters1, + SomeVeryLongNameAndAllOfItsAdditionalLetters2, +) +# bugfix_3 +__all__ = ( + base_events.__all__ + + coroutines.__all__ + + events.__all__ + + futures.__all__ + + locks.__all__ + + protocols.__all__ + + runners.__all__ + + queues.__all__ + + streams.__all__ + + tasks.__all__ +) diff --git a/tests/test_black.py b/tests/test_black.py index 828b3e45bf9..e38374a7aeb 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -596,6 +596,14 @@ def test_tuple_assign(self) -> None: black.assert_equivalent(source, actual) black.assert_stable(source, actual, black.FileMode()) + @patch("black.dump_to_file", dump_to_stderr) + def test__maybe_empty_lines(self) -> None: + source, expected = read_data("import_handling_issue_251") + actual = fs(source, mode=black.FileMode(import_mode=1)) + self.assertFormatEqual(expected, actual) + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, black.FileMode(import_mode=1)) + def test_tab_comment_indentation(self) -> None: contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n" contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"