Ensure that from-style imports are always ordered first in __future__ (#9039)

Closes https://github.com/astral-sh/ruff/issues/8823.
This commit is contained in:
Charlie Marsh 2023-12-06 22:56:23 -05:00 committed by GitHub
parent d22ce5372d
commit 946b308197
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 70 additions and 2 deletions

View file

@ -0,0 +1,2 @@
import __future__
from __future__ import annotations

View file

@ -0,0 +1,2 @@
import __future__
from __future__ import annotations

View file

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

View file

@ -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<EitherImport<'a>> {
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))

View file

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

View file

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