diff --git a/isort/wrap.py b/isort/wrap.py index cd6db86a..e79aa410 100644 --- a/isort/wrap.py +++ b/isort/wrap.py @@ -68,6 +68,8 @@ def import_statement( return statement +# Ignore DeepSource cyclomatic complexity check for this function. +# skipcq: PY-R1000 def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> str: """Returns a line wrapped to the specified line-length, if possible.""" if len(content) <= config.line_length: @@ -84,15 +86,11 @@ def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> if "#" in content: line_without_comment, comment = content.split("#", 1) - # A ``from ... import *`` / ``from ... cimport *`` statement cannot use - # parenthesis-based wrapping because the wildcard ``*`` has no valid - # continuation in that mode. Use a backslash continuation instead so the - # statement is split across lines while remaining valid Python. - # See https://github.com/PyCQA/isort/issues/2267 - if line_without_comment.rstrip().endswith("*"): - prefix, keyword, _ = line_without_comment.rstrip().rsplit(" ", 2) - comment_suffix = f" #{comment}" if comment else "" - return f"{prefix} {keyword} \\{line_separator}{config.indent}*{comment_suffix}" + # Star imports cannot use parenthesized wrapping, while backslash wrapping + # conflicts with Black. Leave them intact even when they exceed line length. + # See https://github.com/PyCQA/isort/issues/2267 and issue #2649. + if line_without_comment.rstrip().endswith("import *"): + return content for splitter in ("import ", "cimport ", ".", "as "): exp = r"\b" + re.escape(splitter) + r"\b" diff --git a/tests/unit/test_wrap.py b/tests/unit/test_wrap.py index f922e7a8..80cfe6f2 100644 --- a/tests/unit/test_wrap.py +++ b/tests/unit/test_wrap.py @@ -50,33 +50,25 @@ def test_line__comment_with_brackets__expects_unchanged_comment(multi_line_outpu assert wrap.line(content=content, line_separator="\n", config=config) == expected -def test_line_star_import_wrapped_with_backslash() -> None: - """Star imports cannot use parenthesis-based wrapping, so should use backslashes. - - See issue #2267. - """ +def test_line_star_import_is_not_wrapped() -> None: + """Star imports cannot be split in a way that Black accepts (issue #2649).""" content = "from very.very.very.very.very.very.very.very.very.long.line import *" - expected = "from very.very.very.very.very.very.very.very.very.long.line import \\\n *" config = Config(line_length=20) - assert wrap.line(content=content, line_separator="\n", config=config) == expected + assert wrap.line(content=content, line_separator="\n", config=config) == content -def test_line_star_cimport_wrapped_with_backslash() -> None: - """Star cimports should also use backslashes.""" +def test_line_star_cimport_is_not_wrapped() -> None: + """Star cimports have the same syntactic wrapping restriction.""" content = "from very.very.very.very.very.very.very.very.very.long.line cimport *" - expected = "from very.very.very.very.very.very.very.very.very.long.line cimport \\\n *" config = Config(line_length=20) - assert wrap.line(content=content, line_separator="\n", config=config) == expected + assert wrap.line(content=content, line_separator="\n", config=config) == content -def test_line_star_import_with_comment_wrapped_with_backslash() -> None: - """When falling back to backslashes for start imports, comments should be preserved.""" +def test_line_star_import_with_comment_is_not_wrapped() -> None: + """Comments remain on an over-long star import that cannot be wrapped.""" content = "from very.very.very.very.very.very.very.very.very.long.line import * # noqa: F401" config = Config(line_length=20) - expected = ( - "from very.very.very.very.very.very.very.very.very.long.line import \\\n * # noqa: F401" - ) - assert wrap.line(content=content, line_separator="\n", config=config) == expected + assert wrap.line(content=content, line_separator="\n", config=config) == content def test_line_star_import_in_noqa_mode_is_not_backslash_wrapped() -> None: @@ -88,8 +80,7 @@ def test_line_star_import_in_noqa_mode_is_not_backslash_wrapped() -> None: ) -def test_star_import_wrapped_end_to_end() -> None: - """New lines should be preserved at the end of too long start imports.""" +def test_star_import_is_not_wrapped_end_to_end() -> None: + """Long star imports remain stable across isort and Black (issue #2649).""" source = "from very.very.very.very.very.very.very.very.very.long.line import *\n" - expected = "from very.very.very.very.very.very.very.very.very.long.line import \\\n *\n" - assert code(source, line_length=20, force_single_line=True) == expected + assert code(source, profile="black") == source