Skip to content

Commit 59472c9

Browse files
authored
Merge pull request #2488 from PyCQA/copilot/fix-isort-empty-comments
Fix: preserve bare `#` inline comments on imports
2 parents 23f100d + 2646b88 commit 59472c9

4 files changed

Lines changed: 48 additions & 21 deletions

File tree

isort/comments.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
def parse(line: str) -> tuple[str, str]:
1+
def parse(line: str) -> tuple[str, str | None]:
22
"""Parses import lines for comments and returns back the
33
import statement and the associated comment.
4+
5+
Returns ``None`` as the comment when no ``#`` is present in the line,
6+
and an empty string when a bare ``#`` with no text is present.
47
"""
58
comment_start = line.find("#")
69
if comment_start != -1:
710
return (line[:comment_start], line[comment_start + 1 :].strip())
811

9-
return (line, "")
12+
return (line, None)
1013

1114

1215
def add_to_line(
@@ -26,4 +29,7 @@ def add_to_line(
2629
for comment in comments:
2730
if comment not in unique_comments:
2831
unique_comments.append(comment)
29-
return f"{parse(original_string)[0]}{comment_prefix} {'; '.join(unique_comments)}"
32+
comment_text = "; ".join(unique_comments)
33+
if comment_text:
34+
return f"{parse(original_string)[0]}{comment_prefix} {comment_text}"
35+
return f"{parse(original_string)[0]}{comment_prefix}"

isort/output.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,10 @@ def _with_from_imports(
340340
comment = (
341341
parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None)
342342
)
343-
if comment:
343+
if comment is not None:
344+
comment_text = f" {comment}" if comment else ""
344345
single_import_line += (
345-
f"{(comments and ';') or config.comment_prefix} {comment}"
346+
f"{(comments and ';') or config.comment_prefix}{comment_text}"
346347
)
347348
if from_import in as_imports:
348349
if (
@@ -410,7 +411,7 @@ def _with_from_imports(
410411
.get(module, {})
411412
.pop(from_import, None)
412413
)
413-
if specific_comment:
414+
if specific_comment is not None:
414415
from_comments.append(specific_comment)
415416
output.append(
416417
wrap.line(
@@ -432,7 +433,7 @@ def _with_from_imports(
432433
.get(module, {})
433434
.pop(as_import, None)
434435
)
435-
if specific_comment:
436+
if specific_comment is not None:
436437
from_comments.append(specific_comment)
437438

438439
output.append(
@@ -465,7 +466,7 @@ def _with_from_imports(
465466
comment = (
466467
parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None)
467468
)
468-
if comment:
469+
if comment is not None:
469470
# If the comment is a noqa and hanging indent wrapping is used,
470471
# keep the name in the main list and hoist the comment to the statement.
471472
if (
@@ -488,8 +489,9 @@ def _with_from_imports(
488489
removed=config.ignore_comments,
489490
comment_prefix=config.comment_prefix,
490491
)
492+
comment_text = f" {comment}" if comment else ""
491493
single_import_line += (
492-
f"{(use_comments and ';') or config.comment_prefix} {comment}"
494+
f"{(use_comments and ';') or config.comment_prefix}{comment_text}"
493495
)
494496
output.append(wrap.line(single_import_line, parsed.line_separator, config))
495497

@@ -610,17 +612,17 @@ def _with_straight_imports(
610612
inline_comments.extend(parsed.categorized_comments["straight"][module])
611613

612614
combined_straight_imports = ", ".join(straight_modules)
613-
if inline_comments:
614-
combined_inline_comments = " ".join(inline_comments)
615-
else:
616-
combined_inline_comments = ""
617615

618616
output.extend(above_comments)
619617

620-
if combined_inline_comments:
621-
output.append(
622-
f"{import_type} {combined_straight_imports} # {combined_inline_comments}"
623-
)
618+
if inline_comments:
619+
combined_inline_comments = " ".join(c for c in inline_comments if c)
620+
if combined_inline_comments:
621+
output.append(
622+
f"{import_type} {combined_straight_imports} # {combined_inline_comments}"
623+
)
624+
else:
625+
output.append(f"{import_type} {combined_straight_imports} #")
624626
else:
625627
output.append(f"{import_type} {combined_straight_imports}")
626628

isort/parse.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte
189189
import_index = index - 1
190190
nested_comments = {}
191191
import_string, comment = parse_comments(line)
192-
comments = [comment] if comment else []
192+
comments = [comment] if comment is not None else []
193193
line_parts = [part for part in strip_syntax(import_string).strip().split(" ") if part]
194194
if type_of_import == "from" and len(line_parts) == 2 and comments:
195195
nested_comments[line_parts[-1]] = comments[0]
@@ -208,7 +208,7 @@ def _get_next_line() -> tuple[str, str | None]:
208208
for extra_line in extra_lines:
209209
raw_lines.append(extra_line.line)
210210
# If during parsing of the continuation lines we encounter a comment, we record it.
211-
if extra_line.comment:
211+
if extra_line.comment is not None:
212212
comments.append(extra_line.comment)
213213
stripped_line = strip_syntax(extra_line.line).strip()
214214
if (
@@ -253,7 +253,7 @@ def _get_next_line() -> tuple[str, str | None]:
253253

254254
full_name = f"{nested_module} as {as_name}"
255255
associated_comment = nested_comments.get(full_name)
256-
if associated_comment:
256+
if associated_comment is not None:
257257
categorized_comments["nested"].setdefault(top_level_module, {})[
258258
full_name
259259
] = associated_comment
@@ -308,7 +308,7 @@ def _get_next_line() -> tuple[str, str | None]:
308308
root = imports[placed_module][type_of_import]
309309
for import_name in just_imports:
310310
associated_comment = nested_comments.get(import_name)
311-
if associated_comment:
311+
if associated_comment is not None:
312312
categorized_comments["nested"].setdefault(import_from, {})[import_name] = (
313313
associated_comment
314314
)

tests/unit/test_ticketed_features.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,3 +1195,22 @@ def test_sort_separate_packages_issue_2104():
11951195
from package2 import bar
11961196
"""
11971197
)
1198+
1199+
1200+
def test_isort_preserves_empty_inline_comments() -> None:
1201+
"""isort should not strip empty inline comments (bare `#`).
1202+
See: https://github.com/PyCQA/isort/issues/1913
1203+
"""
1204+
# Straight imports
1205+
assert isort.code("import a #\n") == "import a #\n"
1206+
1207+
# From imports
1208+
assert isort.code("from foo import bar #\n") == "from foo import bar #\n"
1209+
1210+
# Should be idempotent
1211+
assert isort.code(isort.code("import a #\n")) == "import a #\n"
1212+
assert isort.code(isort.code("from foo import bar #\n")) == "from foo import bar #\n"
1213+
1214+
# Regular (non-empty) comments should still work
1215+
assert isort.code("import a # comment\n") == "import a # comment\n"
1216+
assert isort.code("from foo import bar # comment\n") == "from foo import bar # comment\n"

0 commit comments

Comments
 (0)