Skip to content

Commit 0377e89

Browse files
committed
fix: preserve from_body comments on as-only emit paths
Fold single-line body comments via with_comments instead of orphan indented lines, and drain remaining body comments onto the last emitted statement when no grouped import_statement is built. Signed-off-by: Alex Chen <l46983284@gmail.com>
1 parent ef4d567 commit 0377e89

2 files changed

Lines changed: 64 additions & 11 deletions

File tree

isort/output.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,13 +288,20 @@ def _inject_from_body_comments(
288288
body_comments: list[str],
289289
line_separator: str,
290290
indent: str,
291+
*,
292+
comment_prefix: str,
293+
ignore_comments: bool = False,
291294
) -> str:
292295
"""Re-insert comment-only lines inside a multi-line from-import statement.
293296
294297
Comment-only members of a parenthesised import group must stay as their own
295298
indented lines. Collapsing them onto the opening ``import (`` line produces
296299
a single long ``# a,; b,; c`` comment that breaks line-length checkers.
297300
See issue #1852.
301+
302+
When the statement is single-line (no closing ``)``), fold the body comments
303+
onto that statement with ``with_comments`` instead of emitting orphan
304+
indented comment lines. Orphan lines are non-idempotent under a second sort.
298305
"""
299306
if not body_comments:
300307
return import_statement
@@ -312,9 +319,13 @@ def _inject_from_body_comments(
312319
lines[index:index] = comment_lines
313320
return line_separator.join(lines)
314321

315-
# Fallback for single-line / non-parenthesised output: keep comments after
316-
# the import statement rather than dropping them.
317-
return line_separator.join([import_statement, *comment_lines])
322+
# Single-line / no-paren fallback: main-compatible fold onto the statement.
323+
return with_comments(
324+
body_comments,
325+
import_statement,
326+
removed=ignore_comments,
327+
comment_prefix=comment_prefix,
328+
)
318329

319330

320331
# Ignore DeepSource cyclomatic complexity check for this function. It was
@@ -756,9 +767,24 @@ def _with_from_imports(
756767
body_comments,
757768
parsed.line_separator,
758769
config.indent,
770+
comment_prefix=config.comment_prefix,
771+
ignore_comments=config.ignore_comments,
759772
)
760773
body_comments = []
761774
output.append(import_statement)
775+
elif body_comments and not config.ignore_comments and output:
776+
# as-import-only / nested-only paths may emit lines without a final
777+
# grouped import_statement. Never drop body comments: fold them onto
778+
# the last statement emitted for this module.
779+
output[-1] = _inject_from_body_comments(
780+
output[-1],
781+
body_comments,
782+
parsed.line_separator,
783+
config.indent,
784+
comment_prefix=config.comment_prefix,
785+
ignore_comments=config.ignore_comments,
786+
)
787+
body_comments = []
762788
return output
763789

764790

tests/unit/test_regressions.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,18 +2495,45 @@ def test_combine_star_folds_body_comments_issue_1852():
24952495

24962496

24972497
def test_inject_from_body_comments_branches_issue_1852():
2498-
"""The helper retains empty comments and has a lossless single-line fallback."""
2499-
assert _inject_from_body_comments("from foo import alpha", [], "\n", " ") == (
2500-
"from foo import alpha"
2501-
)
2502-
assert _inject_from_body_comments("from foo import alpha", ["disabled", ""], "\n", " ") == (
2503-
"from foo import alpha\n # disabled\n #"
2504-
)
2498+
"""Paren form keeps body lines; single-line form folds via with_comments."""
2499+
assert _inject_from_body_comments(
2500+
"from foo import alpha", [], "\n", " ", comment_prefix=" #"
2501+
) == ("from foo import alpha")
25052502
assert _inject_from_body_comments(
2506-
"from foo import (\n alpha,\n)", ["disabled", ""], "\n", " "
2503+
"from foo import alpha", ["disabled", ""], "\n", " ", comment_prefix=" #"
2504+
) == ("from foo import alpha # disabled; ")
2505+
assert _inject_from_body_comments(
2506+
"from foo import (\n alpha,\n)",
2507+
["disabled", ""],
2508+
"\n",
2509+
" ",
2510+
comment_prefix=" #",
25072511
) == ("from foo import (\n alpha,\n # disabled\n #\n)")
25082512

25092513

2514+
def test_as_import_only_body_comment_preserved_issue_1852():
2515+
"""as-import-only groups must not drop comment-only body members."""
2516+
to_sort = "from foo import (\n alpha as a,\n # disabled\n)\n"
2517+
expected = "from foo import alpha as a # disabled\n"
2518+
first = isort.code(to_sort, profile="black")
2519+
assert first == expected
2520+
assert isort.code(first, profile="black") == first
2521+
2522+
2523+
def test_mixed_as_and_live_body_comment_preserved_issue_1852():
2524+
"""Body comments survive when as-imports emit before remaining live names."""
2525+
to_sort = "from foo import (\n alpha as a,\n # disabled\n beta,\n)\n"
2526+
first = isort.code(to_sort, profile="black")
2527+
assert "# disabled" in first
2528+
assert "alpha as a" in first
2529+
assert "beta" in first
2530+
for line in first.splitlines():
2531+
if line.startswith(" #"):
2532+
assert "(" in first
2533+
assert ")" in first
2534+
assert isort.code(first, profile="black") == first
2535+
2536+
25102537
def test_combine_star_folds_body_comments_when_comments_are_enabled_issue_1852(monkeypatch):
25112538
"""combine_star folds body comments only when comments are retained."""
25122539
parsed = parse.file_contents(

0 commit comments

Comments
 (0)