Preserve trailing-comma layout when sorting reexports - #2605
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 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:
|
DanielNoord
left a comment
There was a problem hiding this comment.
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 :)
4fda57b to
04780d4
Compare
|
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 |
|
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. |
There was a problem hiding this comment.
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.
04780d4 to
032cc0e
Compare
DanielNoord
left a comment
There was a problem hiding this comment.
Nice targeted fix, thanks! Some small nits
|
|
||
|
|
||
| def test_assignment_applies_formatting_function(): | ||
| def formatting_function(code, extension, config): |
There was a problem hiding this comment.
Can you add typing to this test and the one below?
| "SecondClass", | ||
| ) | ||
| """ | ||
| assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output |
There was a problem hiding this comment.
| 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
| "SecondClass", | ||
| ] | ||
| """ | ||
| assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output |
There was a problem hiding this comment.
| 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
left a comment
There was a problem hiding this comment.
Review (verified locally)
I checked this out and ran the suite on head 032cc0eb — 26/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).
| 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) |
There was a problem hiding this comment.
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.
032cc0e to
6cf8134
Compare
|
Thanks for the review. I updated the tests to add the requested type annotations and simplified the two reexport regression assertions to compare against |
Summary
Preserve explicitly multiline
__all__collections with trailing commas when--sort-reexportsruns 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
__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.pyuv run ruff check isort/literal.py tests/unit/test_regressions.pyuv run mypy isort tests./scripts/done.shCloses #2578