Repository navigation
Sort an indented import block that is followed by a less indented import - #2730
Merged
Merged
Conversation
When an import at a smaller indentation ended an indented import block, `process()` switched `indent` to the new indentation before the block collected so far was sorted. The block was then handled as if it were not indented and was left as it was, so for example the two `from typing_extensions import ...` lines of an `else:` branch were not combined when a top-level import followed. The switch now waits until the block has been written, and the block is sorted the same way as when code follows it. Fixes PyCQA#2010
There was a problem hiding this comment.
🟡 Changes recommended
Partial dedents discard existing blank-line separators that the following indented section does not restore.
1 open issue
What changed in this PR
Fixes the sorting issue in #2010 by preserving an import block’s indentation until sorting is complete.
Changes:
- Defers indentation changes until the next import section.
- Adjusts spacing between sections.
- Adds regression tests for top-level and nested dedents, including output stability.
| File | Description |
|---|---|
| tests/unit/test_regressions.py | Adds coverage for indented imports followed by less-indented imports. |
| isort/core.py | Preserves indentation during sorting and adjusts section spacing. |
Review effort: Balanced
Keep existing separators when an indented import block is followed by an import at a less-indented, but still nested, level. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DanielNoord
approved these changes
Oct 21, 2026
DanielNoord
enabled auto-merge
October 21, 2026 20:53
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2730 +/- ##
=======================================
Coverage 99.37% 99.38%
=======================================
Files 40 40
Lines 3224 3272 +48
Branches 689 706 +17
=======================================
+ Hits 3204 3252 +48
Misses 12 12
Partials 8 8 🚀 New features to boost your workflow:
|
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.

Fixes #2010
This is the second part of #2010 that @Eljees described: the
else:imports are not combined, even withoutlines_before_imports.On
main, isort leaves the twofrom typing_extensions import ...lines as they are. If you remove the last line, they are combined.Cause
When an import with less indentation ends an indented import block,
process()setsindentto the new indentation before it sorts the block it has collected so far. So that block is sorted as if it were not indented, and it comes out unchanged.Change
next_indentand applied once the current block has been written. This only happens when the block already holds imports. When the block holds only comments or blank lines,indentstill switches right away, as before.def f():\n import os\n import a\n import os\nsorts to the same lines whetherx = 1orimport bcomes next. The two places that used to readindentafter the early switch now read the next section's indentation:lines_before_imports == -1);lines_before_importsof them).These are the four tests @Eljees mentioned (
test_nested_comment_handling,test_autofix_mixed_indent_imports_1575,test_unindented_comment_in_indented_block_issue_1899,test_isort_should_leave_non_import_from_lines_alone_issue_1488), plustest_lines_before_imports_keeps_import_after_indented_block. All of them pass unchanged.Tests
New
test_indented_imports_followed_by_less_indented_import_issue_2010intests/unit/test_regressions.py. It covers:lines_before_imports;Each case also checks that the output is stable on a second run. The test fails on
mainand passes with the change.What was run (Python 3.14,
uv sync --all-extras --frozen):pytest tests/unit: 695 passed, 1 skipped.pytest tests/integration/test_hypothesmith.py tests/integration/test_literal.py tests/integration/test_setting_combinations.py tests/integration/test_ticketed_features.py: 3 passed.tox -e lintcommands, run one by one:mypy,isort --profile hug --check --diff isort/ tests/ scripts/,flake8 isort/ tests/,ruff check,ruff format --check,bandit -r isort/. All clean.mainand through this branch.maingives the same second-run result there: withlines_before_imports=1, it adds a blank line between# commentand the import below it.Not run:
tests/integration/test_projects_using_isort.py, which clones other projects.