mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 15:14:42 +00:00
Always place non-relative imports after relative imports (#10669)
## Summary When `relative-imports-order = "closest-to-furthest"` is set, we should _still_ put non-relative imports after relative imports. It's rare for them to be in the same section, but _possible_ if you use `known-local-folder`. Closes https://github.com/astral-sh/ruff/issues/10655. ## Test Plan New tests. Also sorted this file: ```python from ..models import ABC from .models import Question from .utils import create_question from django_polls.apps.polls.models import Choice ``` With both: - `isort view.py` - `ruff check view.py --select I --fix` And the following `pyproject.toml`: ```toml [tool.ruff.lint.isort] order-by-type = false relative-imports-order = "closest-to-furthest" known-local-folder = ["django_polls"] [tool.isort] profile = "black" reverse_relative = true known_local_folder = ["django_polls"] ``` I verified that Ruff and isort gave the same result, and that they _still_ gave the same result when removing the relevant setting: ```toml [tool.ruff.lint.isort] order-by-type = false known-local-folder = ["django_polls"] [tool.isort] profile = "black" known_local_folder = ["django_polls"] ```
This commit is contained in:
parent
fc54f53662
commit
75e01420fa
6 changed files with 85 additions and 20 deletions
|
@ -3,3 +3,5 @@ import ruff
|
||||||
import leading_prefix
|
import leading_prefix
|
||||||
import os
|
import os
|
||||||
from . import leading_prefix
|
from . import leading_prefix
|
||||||
|
from .. import trailing_prefix
|
||||||
|
from ruff import check
|
||||||
|
|
|
@ -471,6 +471,31 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(Path::new("separate_local_folder_imports.py"))]
|
||||||
|
fn known_local_folder_closest(path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("known_local_folder_closest_{}", path.to_string_lossy());
|
||||||
|
let diagnostics = test_path(
|
||||||
|
Path::new("isort").join(path).as_path(),
|
||||||
|
&LinterSettings {
|
||||||
|
isort: super::settings::Settings {
|
||||||
|
known_modules: KnownModules::new(
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![pattern("ruff")],
|
||||||
|
vec![],
|
||||||
|
FxHashMap::default(),
|
||||||
|
),
|
||||||
|
relative_imports_order: RelativeImportsOrder::ClosestToFurthest,
|
||||||
|
..super::settings::Settings::default()
|
||||||
|
},
|
||||||
|
src: vec![test_resource_path("fixtures/isort")],
|
||||||
|
..LinterSettings::for_rule(Rule::UnsortedImports)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
assert_messages!(snapshot, diagnostics);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case(Path::new("case_sensitive.py"))]
|
#[test_case(Path::new("case_sensitive.py"))]
|
||||||
fn case_sensitive(path: &Path) -> Result<()> {
|
fn case_sensitive(path: &Path) -> Result<()> {
|
||||||
let snapshot = format!("case_sensitive_{}", path.to_string_lossy());
|
let snapshot = format!("case_sensitive_{}", path.to_string_lossy());
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||||
|
---
|
||||||
|
separate_local_folder_imports.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||||
|
|
|
||||||
|
1 | / import sys
|
||||||
|
2 | | import ruff
|
||||||
|
3 | | import leading_prefix
|
||||||
|
4 | | import os
|
||||||
|
5 | | from . import leading_prefix
|
||||||
|
6 | | from .. import trailing_prefix
|
||||||
|
7 | | from ruff import check
|
||||||
|
|
|
||||||
|
= help: Organize imports
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
1 |+import os
|
||||||
|
1 2 | import sys
|
||||||
|
3 |+
|
||||||
|
4 |+import leading_prefix
|
||||||
|
5 |+
|
||||||
|
2 6 | import ruff
|
||||||
|
3 |-import leading_prefix
|
||||||
|
4 |-import os
|
||||||
|
5 7 | from . import leading_prefix
|
||||||
|
6 8 | from .. import trailing_prefix
|
||||||
|
7 9 | from ruff import check
|
|
@ -8,6 +8,8 @@ separate_local_folder_imports.py:1:1: I001 [*] Import block is un-sorted or un-f
|
||||||
3 | | import leading_prefix
|
3 | | import leading_prefix
|
||||||
4 | | import os
|
4 | | import os
|
||||||
5 | | from . import leading_prefix
|
5 | | from . import leading_prefix
|
||||||
|
6 | | from .. import trailing_prefix
|
||||||
|
7 | | from ruff import check
|
||||||
|
|
|
|
||||||
= help: Organize imports
|
= help: Organize imports
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ separate_local_folder_imports.py:1:1: I001 [*] Import block is un-sorted or un-f
|
||||||
2 6 | import ruff
|
2 6 | import ruff
|
||||||
3 |-import leading_prefix
|
3 |-import leading_prefix
|
||||||
4 |-import os
|
4 |-import os
|
||||||
5 7 | from . import leading_prefix
|
7 |+from .. import trailing_prefix
|
||||||
|
5 8 | from . import leading_prefix
|
||||||
|
6 |-from .. import trailing_prefix
|
||||||
|
7 9 | from ruff import check
|
||||||
|
|
|
@ -8,18 +8,22 @@ separate_local_folder_imports.py:1:1: I001 [*] Import block is un-sorted or un-f
|
||||||
3 | | import leading_prefix
|
3 | | import leading_prefix
|
||||||
4 | | import os
|
4 | | import os
|
||||||
5 | | from . import leading_prefix
|
5 | | from . import leading_prefix
|
||||||
|
6 | | from .. import trailing_prefix
|
||||||
|
7 | | from ruff import check
|
||||||
|
|
|
|
||||||
= help: Organize imports
|
= help: Organize imports
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 |+import os
|
1 |+import os
|
||||||
1 2 | import sys
|
1 2 | import sys
|
||||||
3 |+
|
3 |+
|
||||||
2 4 | import ruff
|
2 4 | import ruff
|
||||||
5 |+
|
5 |+from ruff import check
|
||||||
3 6 | import leading_prefix
|
6 |+
|
||||||
4 |-import os
|
3 7 | import leading_prefix
|
||||||
7 |+
|
4 |-import os
|
||||||
5 8 | from . import leading_prefix
|
8 |+
|
||||||
|
9 |+from .. import trailing_prefix
|
||||||
|
5 10 | from . import leading_prefix
|
||||||
|
6 |-from .. import trailing_prefix
|
||||||
|
7 |-from ruff import check
|
||||||
|
|
|
@ -69,6 +69,7 @@ impl<'a> From<String> for NatOrdStr<'a> {
|
||||||
pub(crate) enum Distance {
|
pub(crate) enum Distance {
|
||||||
Nearest(u32),
|
Nearest(u32),
|
||||||
Furthest(Reverse<u32>),
|
Furthest(Reverse<u32>),
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
|
@ -101,17 +102,20 @@ impl<'a> ModuleKey<'a> {
|
||||||
style: ImportStyle,
|
style: ImportStyle,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let level = level.unwrap_or_default();
|
|
||||||
|
|
||||||
let force_to_top = !name.is_some_and(|name| settings.force_to_top.contains(name)); // `false` < `true` so we get forced to top first
|
let force_to_top = !name.is_some_and(|name| settings.force_to_top.contains(name)); // `false` < `true` so we get forced to top first
|
||||||
|
|
||||||
let maybe_length = (settings.length_sort
|
let maybe_length = (settings.length_sort
|
||||||
|| (settings.length_sort_straight && style == ImportStyle::Straight))
|
|| (settings.length_sort_straight && style == ImportStyle::Straight))
|
||||||
.then_some(name.map(str::width).unwrap_or_default() + level as usize);
|
.then_some(
|
||||||
|
name.map(str::width).unwrap_or_default() + level.unwrap_or_default() as usize,
|
||||||
|
);
|
||||||
|
|
||||||
let distance = match settings.relative_imports_order {
|
let distance = match level {
|
||||||
RelativeImportsOrder::ClosestToFurthest => Distance::Nearest(level),
|
None | Some(0) => Distance::None,
|
||||||
RelativeImportsOrder::FurthestToClosest => Distance::Furthest(Reverse(level)),
|
Some(level) => match settings.relative_imports_order {
|
||||||
|
RelativeImportsOrder::ClosestToFurthest => Distance::Nearest(level),
|
||||||
|
RelativeImportsOrder::FurthestToClosest => Distance::Furthest(Reverse(level)),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let maybe_lowercase_name = name.and_then(|name| {
|
let maybe_lowercase_name = name.and_then(|name| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue