Skip to content

Preserve trailing-comma layout when sorting reexports - #2605

Open
PSR94 wants to merge 1 commit into
PyCQA:mainfrom
PSR94:issue/2578-preserve-reexport-trailing-comma
Open

Preserve trailing-comma layout when sorting reexports#2605
PSR94 wants to merge 1 commit into
PyCQA:mainfrom
PSR94:issue/2578-preserve-reexport-trailing-comma

Conversation

@PSR94

@PSR94 PSR94 commented Aug 2, 2026

Copy link
Copy Markdown

Summary

Preserve explicitly multiline __all__ collections with trailing commas when --sort-reexports runs under trailing-comma-aware formatting such as the Black profile.

Why this helps

This keeps sorted reexports stable with Black's magic trailing comma behavior, so short __all__ declarations that are intentionally one item per line are not collapsed after sorting.

Changes made

  • Detect when a source literal is multiline and ends with a trailing comma.
  • Preserve that multiline layout when the active config includes trailing commas.
  • Add regression coverage for short tuple-style and list-style __all__ exports.

Testing

  • uv run pytest tests/unit/test_regressions.py -k '2578 or 2280'
  • uv run pytest tests/unit/test_isort.py -k 'reexport_multiline'
  • uv run ruff format --check isort/literal.py tests/unit/test_regressions.py
  • uv run ruff check isort/literal.py tests/unit/test_regressions.py
  • uv run mypy isort tests
  • ./scripts/done.sh

Closes #2578

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.43%. Comparing base (d5f40fa) to head (032cc0e).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2605      +/-   ##
==========================================
+ Coverage   99.36%   99.43%   +0.06%     
==========================================
  Files          41       41              
  Lines        3165     3174       +9     
  Branches      682      684       +2     
==========================================
+ Hits         3145     3156      +11     
+ Misses         12       11       -1     
+ Partials        8        7       -1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@DanielNoord DanielNoord left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think you can get full coverage on this? I have not looked at the code yet but we prefer all new code to be covered by tests :)

@PSR94
PSR94 force-pushed the issue/2578-preserve-reexport-trailing-comma branch from 4fda57b to 04780d4 Compare August 4, 2026 02:32
@PSR94

PSR94 commented Aug 4, 2026

Copy link
Copy Markdown
Author

Thanks for the review. I updated the PR to add coverage for the new literal formatting paths, including the formatting-function branch and the trailing-comma detection guard. I also reran uv run ruff format --check isort/literal.py tests/unit/test_literal.py tests/unit/test_regressions.py, uv run ruff check isort/literal.py tests/unit/test_literal.py tests/unit/test_regressions.py, uv run mypy isort tests, and ./scripts/done.sh; they are passing, and isort/literal.py reports 100% coverage locally.

@DanielNoord

DanielNoord commented Aug 11, 2026

Copy link
Copy Markdown
Member

I'm a bit pressed for time currently. To keep moving this forward I'll ask Copilot for an initial review. Please disregard if it is bogus, it tends to help me by doing an initial pass but of course it isn't always the quality we hope for.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Preserves multiline trailing-comma layouts when sorting reexports under compatible profiles such as Black.

Changes:

  • Detects multiline literals with trailing commas.
  • Prevents eligible collections from collapsing to one line.
  • Adds tuple/list regression and helper coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
isort/literal.py Detects and preserves trailing-comma layouts.
tests/unit/test_literal.py Tests formatting callbacks and bracket detection.
tests/unit/test_regressions.py Covers short tuple/list __all__ reexports.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@DanielNoord
DanielNoord force-pushed the issue/2578-preserve-reexport-trailing-comma branch from 04780d4 to 032cc0e Compare August 17, 2026 20:56

@DanielNoord DanielNoord left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice targeted fix, thanks! Some small nits

Comment thread tests/unit/test_literal.py Outdated


def test_assignment_applies_formatting_function():
def formatting_function(code, extension, config):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add typing to this test and the one below?

Comment thread tests/unit/test_regressions.py Outdated
"SecondClass",
)
"""
assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output
assert isort.code(test_input, profile="black", sort_reexports=True) == test_input

And then remove expected_output

Comment thread tests/unit/test_regressions.py Outdated
"SecondClass",
]
"""
assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output
assert isort.code(test_input, profile="black", sort_reexports=True) == test_input

Same here

@Manny7717 Manny7717 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (verified locally)

I checked this out and ran the suite on head 032cc0eb26/26 in tests/unit/test_literal.py and 6/6 reexport/regression tests pass (test_sort_reexports_preserves_short_multiline_* included).

The gating on config.include_trailing_comma is the right call: it keeps the default-config behavior intact (the existing test_reexport_multiline* tests that expect short multiline __all__ to collapse still hold), and under the black profile it matches black's magic-trailing-comma semantics exactly — a trailing comma forces the exploded form even when the literal would fit on one line.

One scope question to confirm intent on (inline): the flag now applies to all collection sorters (dict/list/set/tuple/unique-*), not just the reexport path. I think that is consistent with Black's magic trailing comma behavior for any literal with a trailing comma, so it reads as deliberate — just wanted to make sure the broader blast radius (e.g. sort_dicts on data literals that previously collapsed) is intended.

Overlap disclosure: Manny7717 also has #2644 open, which fixes the same issue (#2578) with the same mechanism (a preserve_* flag threaded through _format_collection, gated on include_trailing_comma). Since #2605 predates it and is already in your review loop, I am closing #2644 in favor of this one — no need to reconcile both. Nice targeted fix — thanks for taking it further than I did (the _has_trailing_comma helper with the bracket-matching guard is a cleaner formulation than my newline-only check).

Comment thread isort/literal.py
variable_name, literal = code.split("=")
variable_name = variable_name.strip()
literal = literal.lstrip()
preserve_trailing_comma = config.include_trailing_comma and _has_trailing_comma(literal)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior-change scope: preserve_trailing_comma is now honored by every registered collection sorter (dict, list, set, tuple, unique-list, unique-tuple), so any trailing-comma literal sorted under a trailing-comma-aware config keeps its exploded form — not just all reexports. I believe this is the correct Black-magic-trailing-comma semantics, just confirming the wider blast radius (e.g. data dicts that previously collapsed to one line) is deliberate.

@PSR94
PSR94 force-pushed the issue/2578-preserve-reexport-trailing-comma branch from 032cc0e to 6cf8134 Compare September 1, 2026 23:09
@PSR94

PSR94 commented Sep 1, 2026

Copy link
Copy Markdown
Author

Thanks for the review. I updated the tests to add the requested type annotations and simplified the two reexport regression assertions to compare against test_input directly. I reran the targeted literal/reexport tests, ruff format/check, mypy for the touched tests, and ./scripts/done.sh; they are passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--sort-reexports does not retain trailing commas in short __all__ exports

4 participants