diff --git a/crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py b/crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py new file mode 100644 index 0000000000000..3698d72a9c55e --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py @@ -0,0 +1,2 @@ +import __future__ +from __future__ import annotations diff --git a/crates/ruff_linter/resources/test/fixtures/isort/future_from.py b/crates/ruff_linter/resources/test/fixtures/isort/future_from.py new file mode 100644 index 0000000000000..3698d72a9c55e --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/isort/future_from.py @@ -0,0 +1,2 @@ +import __future__ +from __future__ import annotations diff --git a/crates/ruff_linter/src/rules/isort/mod.rs b/crates/ruff_linter/src/rules/isort/mod.rs index 60b511847c909..0cb8655c13cd6 100644 --- a/crates/ruff_linter/src/rules/isort/mod.rs +++ b/crates/ruff_linter/src/rules/isort/mod.rs @@ -180,7 +180,7 @@ fn format_import_block( continue; }; - let imports = order_imports(import_block, settings); + let imports = order_imports(import_block, import_section, settings); // Add a blank line between every section. if is_first_block { @@ -291,6 +291,7 @@ mod tests { #[test_case(Path::new("force_sort_within_sections.py"))] #[test_case(Path::new("force_to_top.py"))] #[test_case(Path::new("force_wrap_aliases.py"))] + #[test_case(Path::new("future_from.py"))] #[test_case(Path::new("if_elif_else.py"))] #[test_case(Path::new("import_from_after_import.py"))] #[test_case(Path::new("inline_comments.py"))] @@ -701,6 +702,7 @@ mod tests { #[test_case(Path::new("force_sort_within_sections.py"))] #[test_case(Path::new("force_sort_within_sections_with_as_names.py"))] + #[test_case(Path::new("force_sort_within_sections_future.py"))] fn force_sort_within_sections(path: &Path) -> Result<()> { let snapshot = format!("force_sort_within_sections_{}", path.to_string_lossy()); let mut diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/isort/order.rs b/crates/ruff_linter/src/rules/isort/order.rs index 25430b84ad6f9..7f1f6f97fccb3 100644 --- a/crates/ruff_linter/src/rules/isort/order.rs +++ b/crates/ruff_linter/src/rules/isort/order.rs @@ -1,4 +1,5 @@ use crate::rules::isort::sorting::ImportStyle; +use crate::rules::isort::{ImportSection, ImportType}; use itertools::Itertools; use super::settings::Settings; @@ -8,6 +9,7 @@ use super::types::{AliasData, CommentSet, ImportBlock, ImportFromStatement}; pub(crate) fn order_imports<'a>( block: ImportBlock<'a>, + section: &ImportSection, settings: &Settings, ) -> Vec> { let straight_imports = block.import.into_iter(); @@ -52,7 +54,35 @@ pub(crate) fn order_imports<'a>( }, ); - let ordered_imports = if settings.force_sort_within_sections { + let ordered_imports = if matches!(section, ImportSection::Known(ImportType::Future)) { + from_imports + .sorted_by_cached_key(|(import_from, _, _, aliases)| { + ModuleKey::from_module( + import_from.module, + None, + import_from.level, + aliases.first().map(|(alias, _)| (alias.name, alias.asname)), + ImportStyle::From, + settings, + ) + }) + .map(ImportFrom) + .chain( + straight_imports + .sorted_by_cached_key(|(alias, _)| { + ModuleKey::from_module( + Some(alias.name), + alias.asname, + None, + None, + ImportStyle::Straight, + settings, + ) + }) + .map(Import), + ) + .collect() + } else if settings.force_sort_within_sections { straight_imports .map(Import) .chain(from_imports.map(ImportFrom)) diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__force_sort_within_sections_force_sort_within_sections_future.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__force_sort_within_sections_force_sort_within_sections_future.py.snap new file mode 100644 index 0000000000000..6864d488b75ed --- /dev/null +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__force_sort_within_sections_force_sort_within_sections_future.py.snap @@ -0,0 +1,16 @@ +--- +source: crates/ruff_linter/src/rules/isort/mod.rs +--- +force_sort_within_sections_future.py:1:1: I001 [*] Import block is un-sorted or un-formatted + | +1 | / import __future__ +2 | | from __future__ import annotations + | + = help: Organize imports + +ℹ Safe fix + 1 |+from __future__ import annotations +1 2 | import __future__ +2 |-from __future__ import annotations + + diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__future_from.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__future_from.py.snap new file mode 100644 index 0000000000000..f3f5cd2a3574d --- /dev/null +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__future_from.py.snap @@ -0,0 +1,16 @@ +--- +source: crates/ruff_linter/src/rules/isort/mod.rs +--- +future_from.py:1:1: I001 [*] Import block is un-sorted or un-formatted + | +1 | / import __future__ +2 | | from __future__ import annotations + | + = help: Organize imports + +ℹ Safe fix + 1 |+from __future__ import annotations +1 2 | import __future__ +2 |-from __future__ import annotations + +