Skip to content

Commit fe568c0

Browse files
authored
isort: fix bad interaction between force-sort-within-sections and force-to-top (#3645)
1 parent 7741d43 commit fe568c0

5 files changed

+43
-34
lines changed

crates/ruff/resources/test/fixtures/isort/force_sort_within_sections.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
from c import * # import_from_star
33
import a # import
44
import c.d
5+
from z import z1
56
import b as b1 # import_as
7+
import z
68

79
from ..parent import *
810
from .my import fn

crates/ruff/src/rules/isort/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ mod tests {
665665
&Settings {
666666
isort: super::settings::Settings {
667667
force_sort_within_sections: true,
668+
force_to_top: BTreeSet::from(["z".to_string()]),
668669
..super::settings::Settings::default()
669670
},
670671
src: vec![test_resource_path("fixtures/isort")],

crates/ruff/src/rules/isort/snapshots/ruff__rules__isort__tests__force_sort_within_sections.py.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ expression: diagnostics
1111
row: 1
1212
column: 0
1313
end_location:
14-
row: 12
14+
row: 14
1515
column: 0
1616
fix:
17-
content: "import a # import\nimport b as b1 # import_as\nimport c.d\nfrom a import a1 # import_from\nfrom c import * # import_from_star\n\nfrom ...grandparent import fn3\nfrom ..parent import *\nfrom . import my\nfrom .my import fn\nfrom .my.nested import fn2\n"
17+
content: "import a # import\nimport b as b1 # import_as\nimport c.d\nimport z\nfrom a import a1 # import_from\nfrom c import * # import_from_star\nfrom z import z1\n\nfrom ...grandparent import fn3\nfrom ..parent import *\nfrom . import my\nfrom .my import fn\nfrom .my.nested import fn2\n"
1818
location:
1919
row: 1
2020
column: 0
2121
end_location:
22-
row: 12
22+
row: 14
2323
column: 0
2424
parent: ~
2525

crates/ruff/src/rules/isort/snapshots/ruff__rules__isort__tests__force_sort_within_sections_force_sort_within_sections.py.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ expression: diagnostics
1111
row: 1
1212
column: 0
1313
end_location:
14-
row: 12
14+
row: 14
1515
column: 0
1616
fix:
17-
content: "import a # import\nfrom a import a1 # import_from\nimport b as b1 # import_as\nfrom c import * # import_from_star\nimport c.d\n\nfrom ...grandparent import fn3\nfrom ..parent import *\nfrom . import my\nfrom .my import fn\nfrom .my.nested import fn2\n"
17+
content: "import z\nfrom z import z1\nimport a # import\nfrom a import a1 # import_from\nimport b as b1 # import_as\nfrom c import * # import_from_star\nimport c.d\n\nfrom ...grandparent import fn3\nfrom ..parent import *\nfrom . import my\nfrom .my import fn\nfrom .my.nested import fn2\n"
1818
location:
1919
row: 1
2020
column: 0
2121
end_location:
22-
row: 12
22+
row: 14
2323
column: 0
2424
parent: ~
2525

crates/ruff/src/rules/isort/sorting.rs

+34-28
Original file line numberDiff line numberDiff line change
@@ -44,29 +44,28 @@ fn prefix(
4444
}
4545
}
4646

47+
/// Compare two module names' by their `force-to-top`ness.
48+
fn cmp_force_to_top(name1: &str, name2: &str, force_to_top: &BTreeSet<String>) -> Ordering {
49+
let force_to_top1 = force_to_top.contains(name1);
50+
let force_to_top2 = force_to_top.contains(name2);
51+
force_to_top1.cmp(&force_to_top2).reverse()
52+
}
53+
4754
/// Compare two top-level modules.
4855
pub fn cmp_modules(
4956
alias1: &AliasData,
5057
alias2: &AliasData,
5158
force_to_top: &BTreeSet<String>,
5259
) -> Ordering {
53-
(match (
54-
force_to_top.contains(alias1.name),
55-
force_to_top.contains(alias2.name),
56-
) {
57-
(true, true) => Ordering::Equal,
58-
(false, false) => Ordering::Equal,
59-
(true, false) => Ordering::Less,
60-
(false, true) => Ordering::Greater,
61-
})
62-
.then_with(|| natord::compare_ignore_case(alias1.name, alias2.name))
63-
.then_with(|| natord::compare(alias1.name, alias2.name))
64-
.then_with(|| match (alias1.asname, alias2.asname) {
65-
(None, None) => Ordering::Equal,
66-
(None, Some(_)) => Ordering::Less,
67-
(Some(_), None) => Ordering::Greater,
68-
(Some(asname1), Some(asname2)) => natord::compare(asname1, asname2),
69-
})
60+
cmp_force_to_top(alias1.name, alias2.name, force_to_top)
61+
.then_with(|| natord::compare_ignore_case(alias1.name, alias2.name))
62+
.then_with(|| natord::compare(alias1.name, alias2.name))
63+
.then_with(|| match (alias1.asname, alias2.asname) {
64+
(None, None) => Ordering::Equal,
65+
(None, Some(_)) => Ordering::Less,
66+
(Some(_), None) => Ordering::Greater,
67+
(Some(asname1), Some(asname2)) => natord::compare(asname1, asname2),
68+
})
7069
}
7170

7271
/// Compare two member imports within `StmtKind::ImportFrom` blocks.
@@ -124,15 +123,11 @@ pub fn cmp_import_from(
124123
relative_imports_order,
125124
)
126125
.then_with(|| {
127-
match (
128-
force_to_top.contains(&import_from1.module_name()),
129-
force_to_top.contains(&import_from2.module_name()),
130-
) {
131-
(true, true) => Ordering::Equal,
132-
(false, false) => Ordering::Equal,
133-
(true, false) => Ordering::Less,
134-
(false, true) => Ordering::Greater,
135-
}
126+
cmp_force_to_top(
127+
&import_from1.module_name(),
128+
&import_from2.module_name(),
129+
force_to_top,
130+
)
136131
})
137132
.then_with(|| match (&import_from1.module, import_from2.module) {
138133
(None, None) => Ordering::Equal,
@@ -143,6 +138,17 @@ pub fn cmp_import_from(
143138
})
144139
}
145140

141+
/// Compare an import to an import-from.
142+
fn cmp_import_import_from(
143+
import: &AliasData,
144+
import_from: &ImportFromData,
145+
force_to_top: &BTreeSet<String>,
146+
) -> Ordering {
147+
cmp_force_to_top(import.name, &import_from.module_name(), force_to_top).then_with(|| {
148+
natord::compare_ignore_case(import.name, import_from.module.unwrap_or_default())
149+
})
150+
}
151+
146152
/// Compare two [`EitherImport`] enums which may be [`Import`] or [`ImportFrom`]
147153
/// structs.
148154
pub fn cmp_either_import(
@@ -154,10 +160,10 @@ pub fn cmp_either_import(
154160
match (a, b) {
155161
(Import((alias1, _)), Import((alias2, _))) => cmp_modules(alias1, alias2, force_to_top),
156162
(ImportFrom((import_from, ..)), Import((alias, _))) => {
157-
natord::compare_ignore_case(import_from.module.unwrap_or_default(), alias.name)
163+
cmp_import_import_from(alias, import_from, force_to_top).reverse()
158164
}
159165
(Import((alias, _)), ImportFrom((import_from, ..))) => {
160-
natord::compare_ignore_case(alias.name, import_from.module.unwrap_or_default())
166+
cmp_import_import_from(alias, import_from, force_to_top)
161167
}
162168
(ImportFrom((import_from1, ..)), ImportFrom((import_from2, ..))) => cmp_import_from(
163169
import_from1,

0 commit comments

Comments
 (0)