Skip to content

Keep an import that follows an indented import block with lines_before_imports - #2672

Merged
DanielNoord merged 2 commits into
PyCQA:mainfrom
Eljees:fix/lines-before-imports-drops-import
Oct 15, 2026
Merged

DanielNoord merged 2 commits into
PyCQA:mainfrom
Eljees:fix/lines-before-imports-drops-import

Conversation

@Eljees

@Eljees Eljees commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Refs #2010 (the part where the trailing import disappears).

With lines_before_imports set, isort deletes a top-level import that follows an indented import block:

>>> import isort
>>> print(isort.code("if sys:\n    import a\n\nimport b\n", lines_before_imports=1))
if sys:
    import a

import b is gone, with no error. Without lines_before_imports the same input comes back unchanged. The same thing happens to the file from #2010 when its configuration (which sets lines_before_imports=1) is used: the trailing import third_party_library disappears. Reproduced on main (f00e0919) and on the released 9.0.1, 6.0.1 and 5.13.2.

Why

When an import at a shallower indentation than the current section arrives, process() queues it as next_import_section and sets line = "", so that the current section is flushed on this iteration and the queued one becomes the current section.

The lines_before_imports handling that runs next defers blank lines (if line.strip() == "" and not end_of_file: lines_before += line; continue), and it took that emptied line for a blank line. The iteration was skipped, so the indented section was only flushed at the end of the file. The end-of-file pass flushes one section and then promotes next_import_section, but there is no later iteration to write it, so the queued import was lost.

The change

Only defer a blank line when no next_import_section is pending. One condition in core.py, with a comment saying why.

Test

test_lines_before_imports_keeps_import_after_indented_block covers the minimal case and the one from #2010, and checks that the output is stable when isort runs on it again. On main it fails:

>       assert output.endswith("\nimport b\n")
E       AssertionError: assert False

tests/unit in a clean container: 652 passed, 1 skipped with the change. The same 2 tests fail with and without it (test_isort_supports_shared_profiles_issue_970, test_sort_configurable_sort_issue_1732), because the example profile and sorting plugins are not installed there.

One thing I left alone: with lines_before_imports=1 the kept import now comes after two blank lines, because the indented section keeps its trailing blank line and the next section also gets the configured one. The output is stable across runs, so this is cosmetic, but I can trim it in this PR if you would rather have exactly lines_before_imports blank lines there.

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.37%. Comparing base (986b53f) to head (366824d).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2672   +/-   ##
=======================================
  Coverage   99.37%   99.37%           
=======================================
  Files          40       40           
  Lines        3223     3223           
  Branches      689      689           
=======================================
  Hits         3203     3203           
  Misses         12       12           
  Partials        8        8           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Copilot review overview

🟡 Changes recommended

Explicit spacing remains off by one between transitioned import sections.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Prevents lines_before_imports from dropping imports queued after indented import blocks.

Changes:

  • Flushes the current section when another indentation level is pending.
  • Adds regression coverage for minimal and issue #2010 cases.
File Description
isort/​core.py Prevents incorrect blank-line deferral.
tests/​unit/​test_regressions.py Tests preservation and idempotence.

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

Comment thread isort/core.py

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.

Copilot review overview

🟢 Approved

The fix addresses the import-loss regression with focused, comprehensive tests and preserves configured spacing.

Review effort: Balanced
Findings: None

Resolved since last review (1)

@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.

Please rebase :)

@Eljees
Eljees force-pushed the fix/lines-before-imports-drops-import branch from 8e62704 to 85ddd4d Compare September 28, 2026 08:00
@Eljees

Eljees commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main and force-pushed. The conflict was in tests/unit/test_regressions.py, where both main and this branch append a test at the end - I kept both and annotated mine with -> None, like the rest of the module now does. The new test fails on main without the fix and passes with it.

@DanielNoord
DanielNoord enabled auto-merge October 15, 2026 19:47
…e_imports

When an import at a shallower indentation starts a new section, line is
emptied, and the lines_before_imports handling took it for a blank line and
deferred it. The indented section was then only flushed at end of file and
the section queued behind it was never written, so the import was lost.
…rts is set

The section that follows already gets exactly lines_before_imports blank lines, so the transition separator made it one more than configured. The regression test now checks the full output for 0, 1 and 2.
@DanielNoord
DanielNoord force-pushed the fix/lines-before-imports-drops-import branch from bb54e8e to 366824d Compare October 15, 2026 19:49
@DanielNoord
DanielNoord added this pull request to the merge queue Oct 15, 2026
Merged via the queue into PyCQA:main with commit 5a6aaf5 Oct 15, 2026
37 checks passed
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.

3 participants