Skip to content

Commit ba61bb6

Browse files
authored
Fix isort no-lines-before preceded by an empty section (#3139)
Fix isort no-lines-before preceded by an empty section Fix #3138.
1 parent 17ab71f commit ba61bb6

File tree

4 files changed

+68
-4
lines changed

4 files changed

+68
-4
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from __future__ import annotations
2+
from typing import Any
3+
from . import my_local_folder_object

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ use log::debug;
66
use ruff_python::sys::KNOWN_STANDARD_LIBRARY;
77
use schemars::JsonSchema;
88
use serde::{Deserialize, Serialize};
9+
use strum_macros::EnumIter;
910

1011
use super::types::{ImportBlock, Importable};
1112
use crate::settings::types::PythonVersion;
1213

1314
#[derive(
14-
Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema, Hash,
15+
Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema, Hash, EnumIter,
1516
)]
1617
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
1718
pub enum ImportType {

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

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use normalize::normalize_imports;
1212
use order::order_imports;
1313
use settings::RelativeImportsOrder;
1414
use sorting::cmp_either_import;
15+
use strum::IntoEnumIterator;
1516
use track::{Block, Trailer};
1617
use types::EitherImport::{Import, ImportFrom};
1718
use types::{AliasData, CommentSet, EitherImport, OrderedImportBlock, TrailingComma};
@@ -232,7 +233,7 @@ fn format_import_block(
232233
target_version: PythonVersion,
233234
) -> String {
234235
// Categorize by type (e.g., first-party vs. third-party).
235-
let block_by_type = categorize_imports(
236+
let mut block_by_type = categorize_imports(
236237
block,
237238
src,
238239
package,
@@ -247,7 +248,17 @@ fn format_import_block(
247248

248249
// Generate replacement source code.
249250
let mut is_first_block = true;
250-
for (import_type, import_block) in block_by_type {
251+
let mut pending_lines_before = false;
252+
for import_type in ImportType::iter() {
253+
let import_block = block_by_type.remove(&import_type);
254+
255+
if !no_lines_before.contains(&import_type) {
256+
pending_lines_before = true;
257+
}
258+
let Some(import_block) = import_block else {
259+
continue;
260+
};
261+
251262
let mut imports = order_imports(
252263
import_block,
253264
order_by_type,
@@ -280,8 +291,10 @@ fn format_import_block(
280291
// Add a blank line between every section.
281292
if is_first_block {
282293
is_first_block = false;
283-
} else if !no_lines_before.contains(&import_type) {
294+
pending_lines_before = false;
295+
} else if pending_lines_before {
284296
output.push_str(stylist.line_ending());
297+
pending_lines_before = false;
285298
}
286299

287300
let mut lines_inserted = false;
@@ -791,6 +804,31 @@ mod tests {
791804
Ok(())
792805
}
793806

807+
#[test_case(Path::new("no_lines_before_with_empty_sections.py"))]
808+
fn no_lines_before_with_empty_sections(path: &Path) -> Result<()> {
809+
let snapshot = format!(
810+
"no_lines_before_with_empty_sections.py_{}",
811+
path.to_string_lossy()
812+
);
813+
let mut diagnostics = test_path(
814+
Path::new("isort").join(path).as_path(),
815+
&Settings {
816+
isort: super::settings::Settings {
817+
no_lines_before: BTreeSet::from([
818+
ImportType::StandardLibrary,
819+
ImportType::LocalFolder,
820+
]),
821+
..super::settings::Settings::default()
822+
},
823+
src: vec![test_resource_path("fixtures/isort")],
824+
..Settings::for_rule(Rule::UnsortedImports)
825+
},
826+
)?;
827+
diagnostics.sort_by_key(|diagnostic| diagnostic.location);
828+
assert_yaml_snapshot!(snapshot, diagnostics);
829+
Ok(())
830+
}
831+
794832
#[test_case(Path::new("lines_after_imports_nothing_after.py"))]
795833
#[test_case(Path::new("lines_after_imports_func_after.py"))]
796834
#[test_case(Path::new("lines_after_imports_class_after.py"))]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: crates/ruff/src/rules/isort/mod.rs
3+
expression: diagnostics
4+
---
5+
- kind:
6+
UnsortedImports: ~
7+
location:
8+
row: 1
9+
column: 0
10+
end_location:
11+
row: 4
12+
column: 0
13+
fix:
14+
content: "from __future__ import annotations\nfrom typing import Any\n\nfrom . import my_local_folder_object\n"
15+
location:
16+
row: 1
17+
column: 0
18+
end_location:
19+
row: 4
20+
column: 0
21+
parent: ~
22+

0 commit comments

Comments
 (0)