mirror of
https://github.com/astral-sh/ruff
synced 2026-01-22 22:10:48 -05:00
Add support for combine-as-imports import formatting (#1022)
This commit is contained in:
@@ -145,7 +145,7 @@ fn annotate_imports<'a>(
|
||||
annotated
|
||||
}
|
||||
|
||||
fn normalize_imports(imports: Vec<AnnotatedImport>) -> ImportBlock {
|
||||
fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) -> ImportBlock {
|
||||
let mut block = ImportBlock::default();
|
||||
for import in imports {
|
||||
match import {
|
||||
@@ -191,7 +191,7 @@ fn normalize_imports(imports: Vec<AnnotatedImport>) -> ImportBlock {
|
||||
} => {
|
||||
// Associate the comments with the first alias (best effort).
|
||||
if let Some(alias) = names.first() {
|
||||
if alias.asname.is_none() {
|
||||
if alias.asname.is_none() || combine_as_imports {
|
||||
let entry = &mut block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
@@ -225,7 +225,7 @@ fn normalize_imports(imports: Vec<AnnotatedImport>) -> ImportBlock {
|
||||
|
||||
// Create an entry for every alias.
|
||||
for alias in names {
|
||||
if alias.asname.is_none() {
|
||||
if alias.asname.is_none() || combine_as_imports {
|
||||
let entry = block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
@@ -397,6 +397,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
|
||||
ordered
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn format_imports(
|
||||
block: &[&Stmt],
|
||||
comments: Vec<Comment>,
|
||||
@@ -405,11 +406,12 @@ pub fn format_imports(
|
||||
known_first_party: &BTreeSet<String>,
|
||||
known_third_party: &BTreeSet<String>,
|
||||
extra_standard_library: &BTreeSet<String>,
|
||||
combine_as_imports: bool,
|
||||
) -> String {
|
||||
let block = annotate_imports(block, comments);
|
||||
|
||||
// Normalize imports (i.e., deduplicate, aggregate `from` imports).
|
||||
let block = normalize_imports(block);
|
||||
let block = normalize_imports(block, combine_as_imports);
|
||||
|
||||
// Categorize by type (e.g., first-party vs. third-party).
|
||||
let block_by_type = categorize_imports(
|
||||
@@ -466,9 +468,10 @@ mod tests {
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::Settings;
|
||||
use crate::{isort, Settings};
|
||||
|
||||
#[test_case(Path::new("add_newline_before_comments.py"))]
|
||||
#[test_case(Path::new("combine_as_imports.py"))]
|
||||
#[test_case(Path::new("combine_import_froms.py"))]
|
||||
#[test_case(Path::new("comments.py"))]
|
||||
#[test_case(Path::new("deduplicate_imports.py"))]
|
||||
@@ -490,7 +493,7 @@ mod tests {
|
||||
#[test_case(Path::new("sort_similar_imports.py"))]
|
||||
#[test_case(Path::new("trailing_suffix.py"))]
|
||||
#[test_case(Path::new("type_comments.py"))]
|
||||
fn isort(path: &Path) -> Result<()> {
|
||||
fn default(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}", path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/isort")
|
||||
@@ -506,4 +509,26 @@ mod tests {
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Path::new("combine_as_imports.py"))]
|
||||
fn combine_as_imports(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("combine_as_imports_{}", path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/isort")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
&Settings {
|
||||
isort: isort::settings::Settings {
|
||||
combine_as_imports: true,
|
||||
..isort::settings::Settings::default()
|
||||
},
|
||||
src: vec![Path::new("resources/test/fixtures/isort").to_path_buf()],
|
||||
..Settings::for_rule(CheckCode::I001)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ pub fn check_imports(
|
||||
&settings.isort.known_first_party,
|
||||
&settings.isort.known_third_party,
|
||||
&settings.isort.extra_standard_library,
|
||||
settings.isort.combine_as_imports,
|
||||
);
|
||||
|
||||
if has_leading_content || has_trailing_content {
|
||||
|
||||
@@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub combine_as_imports: Option<bool>,
|
||||
pub known_first_party: Option<Vec<String>>,
|
||||
pub known_third_party: Option<Vec<String>>,
|
||||
pub extra_standard_library: Option<Vec<String>>,
|
||||
@@ -14,6 +15,7 @@ pub struct Options {
|
||||
|
||||
#[derive(Debug, Hash, Default)]
|
||||
pub struct Settings {
|
||||
pub combine_as_imports: bool,
|
||||
pub known_first_party: BTreeSet<String>,
|
||||
pub known_third_party: BTreeSet<String>,
|
||||
pub extra_standard_library: BTreeSet<String>,
|
||||
@@ -22,6 +24,7 @@ pub struct Settings {
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
combine_as_imports: options.combine_as_imports.unwrap_or_default(),
|
||||
known_first_party: BTreeSet::from_iter(options.known_first_party.unwrap_or_default()),
|
||||
known_third_party: BTreeSet::from_iter(options.known_third_party.unwrap_or_default()),
|
||||
extra_standard_library: BTreeSet::from_iter(
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 0
|
||||
fix:
|
||||
content: "from module import CONSTANT, function\nfrom module import Class as C\nfrom module import function as f\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 0
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 0
|
||||
fix:
|
||||
content: "from module import CONSTANT, Class as C, function, function as f\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 0
|
||||
|
||||
Reference in New Issue
Block a user