Fix non-idempotent output with split_on_trailing_comma and a non-default wrap mode - #2554
Merged
DanielNoord merged 1 commit intoJun 7, 2026
Conversation
DanielNoord
reviewed
Jun 3, 2026
|
|
||
| ### Unreleased | ||
|
|
||
| - Fixed unstable output when `split_on_trailing_comma` and `include_trailing_comma` are combined with a non-default `multi_line_output` mode (#2554) @sarathfrancis90 |
| ) | ||
|
|
||
|
|
||
| def test_split_on_trailing_comma_idempotent_with_non_default_wrap_mode(): |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2554 +/- ##
=======================================
Coverage 99.16% 99.16%
=======================================
Files 41 41
Lines 3097 3098 +1
Branches 670 670
=======================================
+ Hits 3071 3072 +1
Misses 14 14
Partials 12 12 🚀 New features to boost your workflow:
|
…ult wrap mode With include_trailing_comma + split_on_trailing_comma enabled, isort appends a trailing comma whenever it wraps an import across multiple lines. The explode was only applied when the *input* already ended in a trailing comma, so the first pass wrapped using the requested multi_line_output mode and only the second pass collapsed to VERTICAL_HANGING_INDENT, leaving the output unstable. Apply the explode based on the effective trailing comma so a single pass reaches a fixpoint for every wrap mode.
sarathfrancis90
force-pushed
the
fix/split-on-trailing-comma-idempotency
branch
from
June 5, 2026 13:58
dbbdc95 to
8dab19d
Compare
Contributor
Author
|
Done — dropped the CHANGELOG entry and rebased onto main. The regression test now sits next to the new |
DanielNoord
approved these changes
Jun 7, 2026
Member
|
Thanks! |
mergify Bot
added a commit
to ArcadeData/arcadedb
that referenced
this pull request
Aug 30, 2026
…[skip ci] Bumps [https://github.com/pycqa/isort](https://github.com/pycqa/isort) from 8.0.1 to 9.0.0. Release notes *Sourced from [https://github.com/pycqa/isort's releases](https://github.com/pycqa/isort/releases).* > 9.0.0 > ----- > > `isort` `9.0.0` is the result of several alpha and beta releases, as well as a small number of fixes added after the last beta. For release notes for the individual alphas and betas we would like to refer to: > > * [9.0.0a2](https://github.com/PyCQA/isort/releases/tag/9.0.0a2) > * [9.0.0a3](https://github.com/PyCQA/isort/releases/tag/9.0.0a3) > * [9.0.0b1](https://github.com/PyCQA/isort/releases/tag/9.0.0b1) > * [9.0.0b2](https://github.com/PyCQA/isort/releases/tag/9.0.0b2) > * [9.0.0b5](https://github.com/PyCQA/isort/releases/tag/9.0.0b5) > > *Skipped version numbers are the result of changes to our release infrastructure that broke while creating the actual releases.* > > Please find below the full release notes for version `9.0.0`, the newest release of `isort` after version `8.0.1`. > > --- > > `isort` `9.0.0` contains some significant changes and improvements. Most notably there is now support for Python 3.15 and its `lazy` imports and we have included several performance improvements as well as providing `mypyc` compiled wheels for all major platforms and supported versions of Python. In our own tests we saw speeds ups of more than 2x and 3x times, but this is highly dependent on the codebase `isort` needs to inspect. > > The full list of changes is as follows: > > 💥 Breaking Changes > ----------------------- > > * Remove logic for deprecated options ([#2498](https://redirect.github.com/pycqa/isort/issues/2498)) [`@DanielNoord`](https://github.com/DanielNoord) > > 🚀 Features > ----------------- > > * Sort lazy import statements ([#2503](https://redirect.github.com/pycqa/isort/issues/2503)) [`@DanielNoord`](https://github.com/DanielNoord) > * Add initial support for Python 3.15 ([#2466](https://redirect.github.com/pycqa/isort/issues/2466)) [`@DanielNoord`](https://github.com/DanielNoord) > * Compile with `mypyc` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2586](https://redirect.github.com/PyCQA/isort/pull/2586) > * Cache calls to `posixpath.abspath` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2606](https://redirect.github.com/PyCQA/isort/pull/2606) > * Consider private `stdlib` modules to be `stdlib` ([#2295](https://redirect.github.com/pycqa/isort/issues/2295)) [`@devdanzin`](https://github.com/devdanzin) > * Add CLI Flag for --forced-separate ([PyCQA/isort#2367](https://redirect.github.com/PyCQA/isort/pull/2367)) [`@hirak99`](https://github.com/hirak99) > * Add separate\_packages option ([#2313](https://redirect.github.com/pycqa/isort/issues/2313)) [`@alex-liang3`](https://github.com/alex-liang3) > > 🪲 Fixes > -------------- > > * Fix inline comment duplication across merged `from X import` lines ([#2499](https://redirect.github.com/pycqa/isort/issues/2499)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix src glob patterns passed via CLI ([#2497](https://redirect.github.com/pycqa/isort/issues/2497)) [`@ReinerBRO`](https://github.com/ReinerBRO) > * Fix opening-line comment moving to alias attribute line on wrapped imports ([#2491](https://redirect.github.com/pycqa/isort/issues/2491)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix multi\_line\_output=3/5 ignored when wrapping single imports with inline comments ([#2474](https://redirect.github.com/pycqa/isort/issues/2474)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix false positive in `check_code` when using `float_to_top` + `add_imports` ([#2492](https://redirect.github.com/pycqa/isort/issues/2492)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix: preserve bare `#` inline comments on imports ([#2488](https://redirect.github.com/pycqa/isort/issues/2488)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix grouping of non-aliased imports when mixed with aliased imports from the same module ([#2470](https://redirect.github.com/pycqa/isort/issues/2470)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix [PyCQA/isort#2500](https://redirect.github.com/PyCQA/isort/issues/2500): trusted publishing by [`@staticdev`](https://github.com/staticdev) in [PyCQA/isort#2521](https://redirect.github.com/PyCQA/isort/pull/2521) > * Fix git\_hook lazy=True option by [`@sparrowt`](https://github.com/sparrowt) in [PyCQA/isort#2542](https://redirect.github.com/PyCQA/isort/pull/2542) > * Honor `# isort: off/on/split` during `float_to_top` preprocessing with CRLF input by [`@DanielNoord`](https://github.com/DanielNoord) with [`@Copilot`](https://github.com/Copilot) in [PyCQA/isort#2553](https://redirect.github.com/PyCQA/isort/pull/2553) > * Fix --sort-reexports crash with non-seekable streams (e.g. stdin) by [`@Abdu-Ahmed`](https://github.com/Abdu-Ahmed) in [PyCQA/isort#2547](https://redirect.github.com/PyCQA/isort/pull/2547) > * Fix non-idempotent output with split\_on\_trailing\_comma and a non-default wrap mode by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2554](https://redirect.github.com/PyCQA/isort/pull/2554) > * Fix non-idempotent NOQA wrap mode with imports that carry their own comment by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2556](https://redirect.github.com/PyCQA/isort/pull/2556) > * Fix NOQA wrap mode accumulating spaces before the comment on each run by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2558](https://redirect.github.com/PyCQA/isort/pull/2558) > * Fix NOQA not added to long imports in several output paths by [`@urayoru113`](https://github.com/urayoru113) in [PyCQA/isort#2568](https://redirect.github.com/PyCQA/isort/pull/2568) > * Honor `# isort: skip` when a `__future__` import is present ([#2092](https://redirect.github.com/pycqa/isort/issues/2092)) by [`@apoorvdarshan`](https://github.com/apoorvdarshan) in [PyCQA/isort#2574](https://redirect.github.com/PyCQA/isort/pull/2574) ... (truncated) Changelog *Sourced from [https://github.com/pycqa/isort's changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md).* > ### 9.0.0 August 26 2026 > > * Remove logic for deprecated options ([#2498](https://redirect.github.com/pycqa/isort/issues/2498)) [`@DanielNoord`](https://github.com/DanielNoord) > * Sort lazy import statements ([#2503](https://redirect.github.com/pycqa/isort/issues/2503)) [`@DanielNoord`](https://github.com/DanielNoord) > * Add initial support for Python 3.15 ([#2466](https://redirect.github.com/pycqa/isort/issues/2466)) [`@DanielNoord`](https://github.com/DanielNoord) > * Compile with `mypyc` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2586](https://redirect.github.com/PyCQA/isort/pull/2586) > * Cache calls to `posixpath.abspath` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2606](https://redirect.github.com/PyCQA/isort/pull/2606) > * Consider private `stdlib` modules to be `stdlib` ([#2295](https://redirect.github.com/pycqa/isort/issues/2295)) [`@devdanzin`](https://github.com/devdanzin) > * Add CLI Flag for --forced-separate ([PyCQA/isort#2367](https://redirect.github.com/PyCQA/isort/pull/2367)) [`@hirak99`](https://github.com/hirak99) > * Add separate\_packages option ([#2313](https://redirect.github.com/pycqa/isort/issues/2313)) [`@alex-liang3`](https://github.com/alex-liang3) > * Fix inline comment duplication across merged `from X import` lines ([#2499](https://redirect.github.com/pycqa/isort/issues/2499)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix src glob patterns passed via CLI ([#2497](https://redirect.github.com/pycqa/isort/issues/2497)) [`@ReinerBRO`](https://github.com/ReinerBRO) > * Fix opening-line comment moving to alias attribute line on wrapped imports ([#2491](https://redirect.github.com/pycqa/isort/issues/2491)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix multi\_line\_output=3/5 ignored when wrapping single imports with inline comments ([#2474](https://redirect.github.com/pycqa/isort/issues/2474)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix false positive in `check_code` when using `float_to_top` + `add_imports` ([#2492](https://redirect.github.com/pycqa/isort/issues/2492)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix: preserve bare `#` inline comments on imports ([#2488](https://redirect.github.com/pycqa/isort/issues/2488)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix grouping of non-aliased imports when mixed with aliased imports from the same module ([#2470](https://redirect.github.com/pycqa/isort/issues/2470)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix [PyCQA/isort#2500](https://redirect.github.com/PyCQA/isort/issues/2500): trusted publishing by [`@staticdev`](https://github.com/staticdev) in [PyCQA/isort#2521](https://redirect.github.com/PyCQA/isort/pull/2521) > * Fix git\_hook lazy=True option by [`@sparrowt`](https://github.com/sparrowt) in [PyCQA/isort#2542](https://redirect.github.com/PyCQA/isort/pull/2542) > * Honor `# isort: off/on/split` during `float_to_top` preprocessing with CRLF input by [`@DanielNoord`](https://github.com/DanielNoord) with [`@Copilot`](https://github.com/Copilot) in [PyCQA/isort#2553](https://redirect.github.com/PyCQA/isort/pull/2553) > * Fix --sort-reexports crash with non-seekable streams (e.g. stdin) by [`@Abdu-Ahmed`](https://github.com/Abdu-Ahmed) in [PyCQA/isort#2547](https://redirect.github.com/PyCQA/isort/pull/2547) > * Fix non-idempotent output with split\_on\_trailing\_comma and a non-default wrap mode by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2554](https://redirect.github.com/PyCQA/isort/pull/2554) > * Fix non-idempotent NOQA wrap mode with imports that carry their own comment by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2556](https://redirect.github.com/PyCQA/isort/pull/2556) > * Fix NOQA wrap mode accumulating spaces before the comment on each run by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2558](https://redirect.github.com/PyCQA/isort/pull/2558) > * Fix NOQA not added to long imports in several output paths by [`@urayoru113`](https://github.com/urayoru113) in [PyCQA/isort#2568](https://redirect.github.com/PyCQA/isort/pull/2568) > * Honor `# isort: skip` when a `__future__` import is present ([#2092](https://redirect.github.com/pycqa/isort/issues/2092)) by [`@apoorvdarshan`](https://github.com/apoorvdarshan) in [PyCQA/isort#2574](https://redirect.github.com/PyCQA/isort/pull/2574) > * fix: make sorted `__all__` and literals black-compatible ([#2280](https://redirect.github.com/pycqa/isort/issues/2280)) by [`@lord-haffi`](https://github.com/lord-haffi) in [PyCQA/isort#2576](https://redirect.github.com/PyCQA/isort/pull/2576) > * Honor skip comments when sorting reexports by [`@sakshichitnis27`](https://github.com/sakshichitnis27) in [PyCQA/isort#2581](https://redirect.github.com/PyCQA/isort/pull/2581) > * Keep aliased import when the plain name carries a comment by [`@sarathfrancis90`](https://github.com/sarathfrancis90) in [PyCQA/isort#2567](https://redirect.github.com/PyCQA/isort/pull/2567) > * fix: don't crash on --config-root without --resolve-all-configs by [`@Gooh456`](https://github.com/Gooh456) in [PyCQA/isort#2597](https://redirect.github.com/PyCQA/isort/pull/2597) > * fix: check\_code misses lines\_before\_imports-only changes ([#2242](https://redirect.github.com/pycqa/isort/issues/2242)) by [`@gaoflow`](https://github.com/gaoflow) in [PyCQA/isort#2563](https://redirect.github.com/PyCQA/isort/pull/2563) > * Fix preservation of form-feed blank lines by [`@utkarshalpha`](https://github.com/utkarshalpha) in [PyCQA/isort#2599](https://redirect.github.com/PyCQA/isort/pull/2599) > * Small clean up and fix inconsistency in handling of `*` imports by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2619](https://redirect.github.com/PyCQA/isort/pull/2619) > * Fix word-wrapping of 'from ... import \*' into invalid Python ([#2267](https://redirect.github.com/pycqa/isort/issues/2267)) by [`@sudorm-rf0`](https://github.com/sudorm-rf0) in [PyCQA/isort#2624](https://redirect.github.com/PyCQA/isort/pull/2624) > * Fix pylint disable-next comments at the start of imports by [`@Neallin-917`](https://github.com/Neallin-917) in [PyCQA/isort#2628](https://redirect.github.com/PyCQA/isort/pull/2628) > * Keep add\_imports below a prefixed module docstring by [`@Eljees`](https://github.com/Eljees) in [PyCQA/isort#2626](https://redirect.github.com/PyCQA/isort/pull/2626) > * Add read the docs configuration ([#2504](https://redirect.github.com/pycqa/isort/issues/2504)) [`@DanielNoord`](https://github.com/DanielNoord) > * Remove unused and broken dependencies ([#2517](https://redirect.github.com/pycqa/isort/issues/2517)) [`@DanielNoord`](https://github.com/DanielNoord) > * Remove `Any` from `parse.py` ([#2516](https://redirect.github.com/pycqa/isort/issues/2516)) [`@DanielNoord`](https://github.com/DanielNoord) > * Bring documentation in line with old documentation ([#2507](https://redirect.github.com/pycqa/isort/issues/2507)) [`@DanielNoord`](https://github.com/DanielNoord) > * Sync profile docs with implementation ([#2495](https://redirect.github.com/pycqa/isort/issues/2495)) [`@copilot-swe-agent`](https://github.com/copilot-swe-agent) > * Fix the playground ([#2494](https://redirect.github.com/pycqa/isort/issues/2494)) [`@DanielNoord7`](https://github.com/DanielNoord7)) [`@hirak99`](https://github.com/hirak99) > * Remove unused \_ENCODING\_PATTERN regex and re import by [`@duriantaco`](https://github.com/duriantaco) in [PyCQA/isort#2525](https://redirect.github.com/PyCQA/isort/pull/2525) > * Remove references to defunct `git_ignore` config by [`@sparrowt`](https://github.com/sparrowt) in [PyCQA/isort#2531](https://redirect.github.com/PyCQA/isort/pull/2531) > * Fix broken relative links in README and CHANGELOG for both GitHub and Sphinx by [`@rohitshinde08`](https://github.com/rohitshinde08) in [PyCQA/isort#2530](https://redirect.github.com/PyCQA/isort/pull/2530) > * Bump vendored `tomli` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2579](https://redirect.github.com/PyCQA/isort/pull/2579) > * Document temporary .isorted files by [`@sapunyangkut`](https://github.com/sapunyangkut) in [PyCQA/isort#2580](https://redirect.github.com/PyCQA/isort/pull/2580) > * Remove `cruft` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2610](https://redirect.github.com/PyCQA/isort/pull/2610) > * Small fixes in preparation for `mypyc` compiled wheels by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2623](https://redirect.github.com/PyCQA/isort/pull/2623) > * Small fixes in preparation for compiling with `mypyc` by [`@DanielNoord`](https://github.com/DanielNoord) in [PyCQA/isort#2625](https://redirect.github.com/PyCQA/isort/pull/2625) ... (truncated) Commits * [`727d119`](PyCQA/isort@727d119) Name the predicate and drive the prefix test off STRING\_PREFIXES * [`dec76bc`](PyCQA/isort@dec76bc) Fix multiline docstring style * [`cb179db`](PyCQA/isort@cb179db) Keep add\_imports below a prefixed module docstring * [`7542f76`](PyCQA/isort@7542f76) test: distinguish pylint disable-next comments * [`9f56767`](PyCQA/isort@9f56767) Fix pylint disable-next comments at top of imports * [`0cba6f6`](PyCQA/isort@0cba6f6) Delete .cruft.json * [`dcdda58`](PyCQA/isort@dcdda58) Remove `if` completely * [`03d3878`](PyCQA/isort@03d3878) Fix check in CI * [`926a147`](PyCQA/isort@926a147) Run less Builds * [`c4ab2e9`](PyCQA/isort@c4ab2e9) Fix `test_importable` for local dev * Additional commits viewable in [compare view](PyCQA/isort@8.0.1...9.0.0) [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
netbsd-srcmastr
pushed a commit
to NetBSD/pkgsrc
that referenced
this pull request
Aug 30, 2026
### 9.0.0 August 26 2026 - Remove logic for deprecated options (#2498) @DanielNoord - Sort lazy import statements (#2503) @DanielNoord - Add initial support for Python 3.15 (#2466) @DanielNoord - Compile with `mypyc` by @DanielNoord in PyCQA/isort#2586 - Cache calls to `posixpath.abspath` by @DanielNoord in PyCQA/isort#2606 - Consider private `stdlib` modules to be `stdlib` (#2295) @devdanzin - Add CLI Flag for --forced-separate (PyCQA/isort#2367) @hirak99 - Add separate_packages option (#2313) @alex-liang3 - Fix inline comment duplication across merged `from X import` lines (#2499) @copilot-swe-agent - Fix src glob patterns passed via CLI (#2497) @ReinerBRO - Fix opening-line comment moving to alias attribute line on wrapped imports (#2491) @copilot-swe-agent - Fix multi_line_output=3/5 ignored when wrapping single imports with inline comments (#2474) @copilot-swe-agent - Fix false positive in `check_code` when using `float_to_top` + `add_imports` (#2492) @copilot-swe-agent - Fix: preserve bare `#` inline comments on imports (#2488) @copilot-swe-agent - Fix grouping of non-aliased imports when mixed with aliased imports from the same module (#2470) @copilot-swe-agent - Fix PyCQA/isort#2500: trusted publishing by @staticdev in PyCQA/isort#2521 - Fix git_hook lazy=True option by @sparrowt in PyCQA/isort#2542 - Honor `# isort: off/on/split` during `float_to_top` preprocessing with CRLF input by @DanielNoord with @Copilot in PyCQA/isort#2553 - Fix --sort-reexports crash with non-seekable streams (e.g. stdin) by @Abdu-Ahmed in PyCQA/isort#2547 - Fix non-idempotent output with split_on_trailing_comma and a non-default wrap mode by @sarathfrancis90 in PyCQA/isort#2554 - Fix non-idempotent NOQA wrap mode with imports that carry their own comment by @sarathfrancis90 in PyCQA/isort#2556 - Fix NOQA wrap mode accumulating spaces before the comment on each run by @sarathfrancis90 in PyCQA/isort#2558 - Fix NOQA not added to long imports in several output paths by @urayoru113 in PyCQA/isort#2568 - Honor `# isort: skip` when a `__future__` import is present (#2092) by @apoorvdarshan in PyCQA/isort#2574 - fix: make sorted `__all__` and literals black-compatible (#2280) by @lord-haffi in PyCQA/isort#2576 - Honor skip comments when sorting reexports by @sakshichitnis27 in PyCQA/isort#2581 - Keep aliased import when the plain name carries a comment by @sarathfrancis90 in PyCQA/isort#2567 - fix: don't crash on --config-root without --resolve-all-configs by @Gooh456 in PyCQA/isort#2597 - fix: check_code misses lines_before_imports-only changes (#2242) by @gaoflow in PyCQA/isort#2563 - Fix preservation of form-feed blank lines by @utkarshalpha in PyCQA/isort#2599 - Small clean up and fix inconsistency in handling of `*` imports by @DanielNoord in PyCQA/isort#2619 - Fix word-wrapping of 'from ... import *' into invalid Python (#2267) by @sudorm-rf0 in PyCQA/isort#2624 - Fix pylint disable-next comments at the start of imports by @Neallin-917 in PyCQA/isort#2628 - Keep add_imports below a prefixed module docstring by @Eljees in PyCQA/isort#2626 - Add read the docs configuration (#2504) @DanielNoord - Remove unused and broken dependencies (#2517) @DanielNoord - Remove `Any` from `parse.py` (#2516) @DanielNoord - Bring documentation in line with old documentation (#2507) @DanielNoord - Sync profile docs with implementation (#2495) @copilot-swe-agent - Fix the playground (#2494) @DanielNoord7) @hirak99 - Remove unused _ENCODING_PATTERN regex and re import by @duriantaco in PyCQA/isort#2525 - Remove references to defunct `git_ignore` config by @sparrowt in PyCQA/isort#2531 - Fix broken relative links in README and CHANGELOG for both GitHub and Sphinx by @rohitshinde08 in PyCQA/isort#2530 - Bump vendored `tomli` by @DanielNoord in PyCQA/isort#2579 - Document temporary .isorted files by @sapunyangkut in PyCQA/isort#2580 - Remove `cruft` by @DanielNoord in PyCQA/isort#2610 - Small fixes in preparation for `mypyc` compiled wheels by @DanielNoord in PyCQA/isort#2623 - Small fixes in preparation for compiling with `mypyc` by @DanielNoord in PyCQA/isort#2625 - Fix `test_importable` for local dev by @DanielNoord in PyCQA/isort#2635
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
isort's output is not idempotent —
isort(isort(x)) != isort(x)— whensplit_on_trailing_commaandinclude_trailing_commaare both enabled together with a non-defaultmulti_line_outputmode (e.g.GRID,VERTICAL, modes0/1/4/5, or the Black profile with an explicit wrap-mode override). The first pass wraps the import using the requested mode and appends a trailing comma; only the second pass collapses it toVERTICAL_HANGING_INDENT. Running isort twice should always reach a fixpoint, so this is a stability bug.Reproduction
The same instability reproduces for
multi_line_outputmodes0,1,4and5.Root cause
In
isort/output.py, the "explode onto individual lines" branch was only triggered when the input already carried a trailing comma (module in parsed.trailing_commas):But when
include_trailing_commais set, isort itself adds a trailing comma to every import it wraps across multiple lines. So on the first pass the import is wrapped with the requested mode (no explode, because the source had no trailing comma yet) and gains a trailing comma; on the second pass that freshly-added comma now matchesparsed.trailing_commasand the import is exploded — producing different output.Fix
Treat an import as if it already carried a trailing comma when
include_trailing_commais enabled and the statement is going to be wrapped across multiple lines, so the explode happens on the first pass too and the output reaches a fixpoint regardless of the requestedmulti_line_outputmode:This is also consistent with the documented
split_on_trailing_commacontract: an import that ends up with a trailing comma should be split onto separate lines.Behavior impact
split_on_trailing_commaisFalse.do_multiline_reformatstaysFalse).Tests
tests/unit/test_regressions.pyasserting idempotency for the affected configurations. It fails onmainand passes with this change.572 passed, 1 skipped(the single unrelatedtest_importablefailure reproduces identically on pristinemainin this environment and is not touched by this change).ruff check,ruff format --check, andmypyare clean for the changed files.Changelog
Added an entry under
### UnreleasedinCHANGELOG.md.