Skip to content

Commit

Permalink
Limit isort.lines-after-imports to 1 for stub files (astral-sh#9971)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser authored and nkxxll committed Mar 4, 2024
1 parent 90212d5 commit b4ff742
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 2 deletions.
@@ -0,0 +1,16 @@
from __future__ import annotations

from typing import Any

from requests import Session

from my_first_party import my_first_party_object

from . import my_local_folder_object



class Thing(object):
name: str
def __init__(self, name: str):
self.name = name
34 changes: 33 additions & 1 deletion crates/ruff_linter/src/rules/isort/mod.rs
Expand Up @@ -108,7 +108,17 @@ pub(crate) fn format_imports(
output.push_str(block_output.as_str());
}

let lines_after_imports = settings.lines_after_imports;
let lines_after_imports = if source_type.is_stub() {
// Limit the number of lines after imports in stub files to at most 1 to be compatible with the formatter.
// `isort` does the same when using the profile `isort`
match settings.lines_after_imports {
0 => 0,
_ => 1,
}
} else {
settings.lines_after_imports
};

match trailer {
None => {}
Some(Trailer::Sibling) => {
Expand Down Expand Up @@ -975,6 +985,7 @@ mod tests {
}

#[test_case(Path::new("lines_after_imports_nothing_after.py"))]
#[test_case(Path::new("lines_after_imports.pyi"))]
#[test_case(Path::new("lines_after_imports_func_after.py"))]
#[test_case(Path::new("lines_after_imports_class_after.py"))]
fn lines_after_imports(path: &Path) -> Result<()> {
Expand All @@ -995,6 +1006,27 @@ mod tests {
Ok(())
}

#[test_case(Path::new("lines_after_imports.pyi"))]
#[test_case(Path::new("lines_after_imports_func_after.py"))]
#[test_case(Path::new("lines_after_imports_class_after.py"))]
fn lines_after_imports_default_settings(path: &Path) -> Result<()> {
let snapshot = path.to_string_lossy();
let mut diagnostics = test_path(
Path::new("isort").join(path).as_path(),
&LinterSettings {
src: vec![test_resource_path("fixtures/isort")],
isort: super::settings::Settings {
lines_after_imports: -1,
..super::settings::Settings::default()
},
..LinterSettings::for_rule(Rule::UnsortedImports)
},
)?;
diagnostics.sort_by_key(Ranged::start);
assert_messages!(*snapshot, diagnostics);
Ok(())
}

#[test_case(Path::new("lines_between_types.py"))]
fn lines_between_types(path: &Path) -> Result<()> {
let snapshot = format!("lines_between_types{}", path.to_string_lossy());
Expand Down
@@ -0,0 +1,41 @@
---
source: crates/ruff_linter/src/rules/isort/mod.rs
---
lines_after_imports.pyi:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | / from __future__ import annotations
2 | |
3 | | from typing import Any
4 | |
5 | | from requests import Session
6 | |
7 | | from my_first_party import my_first_party_object
8 | |
9 | | from . import my_local_folder_object
10 | |
11 | |
12 | |
13 | | class Thing(object):
| |_^ I001
14 | name: str
15 | def __init__(self, name: str):
|
= help: Organize imports

Safe fix
2 2 |
3 3 | from typing import Any
4 4 |
5 |-from requests import Session
6 |-
7 5 | from my_first_party import my_first_party_object
6 |+from requests import Session
8 7 |
9 8 | from . import my_local_folder_object
10 |-
11 |-
12 9 |
13 10 | class Thing(object):
14 11 | name: str


@@ -0,0 +1,38 @@
---
source: crates/ruff_linter/src/rules/isort/mod.rs
---
lines_after_imports_class_after.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | / from __future__ import annotations
2 | |
3 | | from typing import Any
4 | |
5 | | from requests import Session
6 | |
7 | | from my_first_party import my_first_party_object
8 | |
9 | | from . import my_local_folder_object
10 | | class Thing(object):
| |_^ I001
11 | name: str
12 | def __init__(self, name: str):
|
= help: Organize imports

Safe fix
2 2 |
3 3 | from typing import Any
4 4 |
5 |-from requests import Session
6 |-
7 5 | from my_first_party import my_first_party_object
6 |+from requests import Session
8 7 |
9 8 | from . import my_local_folder_object
9 |+
10 |+
10 11 | class Thing(object):
11 12 | name: str
12 13 | def __init__(self, name: str):


@@ -0,0 +1,55 @@
---
source: crates/ruff_linter/src/rules/isort/mod.rs
---
lines_after_imports_func_after.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | / from __future__ import annotations
2 | |
3 | | from typing import Any
4 | |
5 | | from requests import Session
6 | |
7 | | from my_first_party import my_first_party_object
8 | |
9 | | from . import my_local_folder_object
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | | def main():
| |_^ I001
22 | my_local_folder_object.get()
|
= help: Organize imports

Safe fix
2 2 |
3 3 | from typing import Any
4 4 |
5 |-from requests import Session
6 |-
7 5 | from my_first_party import my_first_party_object
6 |+from requests import Session
8 7 |
9 8 | from . import my_local_folder_object
10 |-
11 |-
12 |-
13 |-
14 |-
15 |-
16 |-
17 |-
18 |-
19 9 |
20 10 |
21 11 | def main():


@@ -0,0 +1,41 @@
---
source: crates/ruff_linter/src/rules/isort/mod.rs
---
lines_after_imports.pyi:1:1: I001 [*] Import block is un-sorted or un-formatted
|
1 | / from __future__ import annotations
2 | |
3 | | from typing import Any
4 | |
5 | | from requests import Session
6 | |
7 | | from my_first_party import my_first_party_object
8 | |
9 | | from . import my_local_folder_object
10 | |
11 | |
12 | |
13 | | class Thing(object):
| |_^ I001
14 | name: str
15 | def __init__(self, name: str):
|
= help: Organize imports

Safe fix
2 2 |
3 3 | from typing import Any
4 4 |
5 |-from requests import Session
6 |-
7 5 | from my_first_party import my_first_party_object
6 |+from requests import Session
8 7 |
9 8 | from . import my_local_folder_object
10 |-
11 |-
12 9 |
13 10 | class Thing(object):
14 11 | name: str


3 changes: 3 additions & 0 deletions crates/ruff_workspace/src/options.rs
Expand Up @@ -2049,6 +2049,9 @@ pub struct IsortOptions {
/// The number of blank lines to place after imports.
/// Use `-1` for automatic determination.
///
/// Ruff uses at most one blank line after imports in typing stub files (files with `.pyi` extension) in accordance to
/// the typing style recommendations ([source](https://typing.readthedocs.io/en/latest/source/stubs.html#blank-lines)).
///
/// When using the formatter, only the values `-1`, `1`, and `2` are compatible because
/// it enforces at least one empty and at most two empty lines after imports.
#[option(
Expand Down
2 changes: 1 addition & 1 deletion ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b4ff742

Please sign in to comment.