From d9028a098b716ab505559e6897d6e44777d70349 Mon Sep 17 00:00:00 2001 From: Bhuminjay Soni Date: Wed, 14 Jan 2026 02:51:18 +0530 Subject: [PATCH] [`isort`] Insert imports in alphabetical order (`I002`) (#22493) ## Summary This PR fixes #20811 , current approach reverses the order in `BtreeSet` however as pointed in https://github.com/astral-sh/ruff/issues/20811#issuecomment-3398958832 here we cannot use I`IndexSet` to preserve config order since Settings derives `CacheKey` which isn't implemented for `IndexSet`, another approach to preserve the original order might be to use `Vec` however lookup time complexity might get affected as a result. I have tested it locally its working as expected , image --------- Signed-off-by: Bhuminjay --- .../rules/isort/rules/add_required_imports.rs | 2 +- ...__tests__required_imports_docstring.py.snap | 16 ++++++++-------- ...__required_imports_multiple_strings.py.snap | 18 +++++++++--------- crates/ruff_linter/src/rules/pyupgrade/mod.rs | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs b/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs index b887e1c2a0..d388fcfe76 100644 --- a/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs +++ b/crates/ruff_linter/src/rules/isort/rules/add_required_imports.rs @@ -140,7 +140,7 @@ pub(crate) fn add_required_imports( source_type: PySourceType, context: &LintContext, ) { - for required_import in &settings.isort.required_imports { + for required_import in settings.isort.required_imports.iter().rev() { add_required_import( required_import, parsed, diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_docstring.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_docstring.py.snap index dc042745fa..c6e1a65cfc 100644 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_docstring.py.snap +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_docstring.py.snap @@ -1,14 +1,6 @@ --- source: crates/ruff_linter/src/rules/isort/mod.rs --- -I002 [*] Missing required import: `from __future__ import annotations` ---> docstring.py:1:1 -help: Insert required import: `from __future__ import annotations` -1 | """Hello, world!""" -2 + from __future__ import annotations -3 | -4 | x = 1 - I002 [*] Missing required import: `from __future__ import generator_stop` --> docstring.py:1:1 help: Insert required import: `from __future__ import generator_stop` @@ -16,3 +8,11 @@ help: Insert required import: `from __future__ import generator_stop` 2 + from __future__ import generator_stop 3 | 4 | x = 1 + +I002 [*] Missing required import: `from __future__ import annotations` +--> docstring.py:1:1 +help: Insert required import: `from __future__ import annotations` +1 | """Hello, world!""" +2 + from __future__ import annotations +3 | +4 | x = 1 diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_multiple_strings.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_multiple_strings.py.snap index f3bad09c67..d23a82f71c 100644 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_multiple_strings.py.snap +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__required_imports_multiple_strings.py.snap @@ -1,15 +1,6 @@ --- source: crates/ruff_linter/src/rules/isort/mod.rs --- -I002 [*] Missing required import: `from __future__ import annotations` ---> multiple_strings.py:1:1 -help: Insert required import: `from __future__ import annotations` -1 | """This is a docstring.""" -2 + from __future__ import annotations -3 | "This is not a docstring." -4 | "This is also not a docstring." -5 | - I002 [*] Missing required import: `from __future__ import generator_stop` --> multiple_strings.py:1:1 help: Insert required import: `from __future__ import generator_stop` @@ -17,4 +8,13 @@ help: Insert required import: `from __future__ import generator_stop` 2 + from __future__ import generator_stop 3 | "This is not a docstring." 4 | "This is also not a docstring." +5 | + +I002 [*] Missing required import: `from __future__ import annotations` +--> multiple_strings.py:1:1 +help: Insert required import: `from __future__ import annotations` +1 | """This is a docstring.""" +2 + from __future__ import annotations +3 | "This is not a docstring." +4 | "This is also not a docstring." 5 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/mod.rs b/crates/ruff_linter/src/rules/pyupgrade/mod.rs index c60d17dfc6..fab22e2ede 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/mod.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/mod.rs @@ -398,17 +398,17 @@ mod tests { 1 + from pipes import Template 2 + from shlex import quote - I002 [*] Missing required import: `from __future__ import generator_stop` - --> :1:1 - help: Insert required import: `from __future__ import generator_stop` - 1 + from __future__ import generator_stop - 2 | from pipes import quote, Template - I002 [*] Missing required import: `from collections import Sequence` --> :1:1 help: Insert required import: `from collections import Sequence` 1 + from collections import Sequence 2 | from pipes import quote, Template + + I002 [*] Missing required import: `from __future__ import generator_stop` + --> :1:1 + help: Insert required import: `from __future__ import generator_stop` + 1 + from __future__ import generator_stop + 2 | from pipes import quote, Template "); }