Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions isort/wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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"
Expand Down
33 changes: 12 additions & 21 deletions tests/unit/test_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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