From 62df020b0b9794368fb911bc480d87fdee793d4a Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Thu, 25 Jun 2026 02:08:16 +0200 Subject: [PATCH] fix: check_code misses lines_before_imports-only changes (#2242) isort.core.process returned False (no changes needed) when the only difference between the input and the correctly-formatted output was the number of blank lines before the import block, controlled by lines_before_imports. Two cooperating issues caused this: 1. _has_changed() used .strip() to compare the before/after import sections. This stripped any leading newlines that output.sorted_imports had prepended (via lines_before_imports), making additions invisible. Fix: use .rstrip() so leading whitespace differences are preserved. 2. When blank lines appear before the first import block and lines_before_imports >= 0, those lines are buffered in lines_before and then discarded (not written) when an import section is present. The raw_import_section passed to _has_changed did not include these consumed blank lines, so removing them was never detected as a change. Fix: capture the buffered lines in above_import_section and prepend them to raw_import_section for the comparison. --- isort/core.py | 7 +++++-- tests/unit/test_isort.py | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/isort/core.py b/isort/core.py index 46bd77f1..a1ad5e6c 100644 --- a/isort/core.py +++ b/isort/core.py @@ -373,12 +373,15 @@ def process( not_imports = True if not_imports: + above_import_section: str = "" if not was_in_quote and config.lines_before_imports > -1: if line.strip() == "" and not end_of_file: lines_before += line continue if not import_section: output_stream.write("".join(lines_before)) + else: + above_import_section = "".join(lines_before) lines_before = [] raw_import_section: str = import_section @@ -451,7 +454,7 @@ def process( ) made_changes = made_changes or _has_changed( - before=raw_import_section, + before=above_import_section + raw_import_section, after=sorted_import_section, line_separator=line_separator, ignore_whitespace=config.ignore_whitespace, @@ -522,4 +525,4 @@ def _has_changed(before: str, after: str, line_separator: str, ignore_whitespace remove_whitespace(before, line_separator=line_separator).strip() != remove_whitespace(after, line_separator=line_separator).strip() ) - return before.strip() != after.strip() + return before.rstrip() != after.rstrip() diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 3271ab53..9575c5a1 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -1703,6 +1703,31 @@ def test_custom_lines_before_import_section(has_body: bool) -> None: ) +def test_check_code_detects_lines_before_import_changes() -> None: + """check_code must return False when lines_before_imports requires adding or removing blank + lines before the import section, even if the imports themselves are already sorted. + + Regression test for https://github.com/PyCQA/isort/issues/2242 + """ + ln = "\n" + sorted_imports = "from a import b, x\n" + body = "\nfoo = 'bar'\n" + + # Adding a blank line before imports (0 present, 1 required) + code_without_blank = sorted_imports + body + assert isort.code(code_without_blank, lines_before_imports=1) == ln + code_without_blank + assert not isort.check_code(code_without_blank, lines_before_imports=1) + + # Removing a blank line before imports (1 present, 0 required) + code_with_blank = ln + sorted_imports + body + assert isort.code(code_with_blank, lines_before_imports=0) == sorted_imports + body + assert not isort.check_code(code_with_blank, lines_before_imports=0) + + # Already correct: no change needed + assert isort.check_code(code_without_blank, lines_before_imports=0) + assert isort.check_code(code_with_blank, lines_before_imports=1) + + def test_custom_lines_after_import_section() -> None: """Test the case where the number of lines to output after imports has been explicitly set.""" test_input = "from a import b\nfoo = 'bar'\n"