[`isort`] Add `constants` and `variables` Options (#1951)

closes https://github.com/charliermarsh/ruff/issues/1819
This commit is contained in:
Maksudul Haque 2023-01-18 18:30:51 +06:00 committed by GitHub
parent cdb4700813
commit 868d0b3e29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 287 additions and 10 deletions

View File

@ -2994,6 +2994,24 @@ combine-as-imports = true
---
#### [`constants`](#constants)
An override list of tokens to always recognize as a CONSTANT
for `order-by-type` regardless of casing.
**Default value**: `[]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff.isort]
constants = ["constant"]
```
---
#### [`extra-standard-library`](#extra-standard-library)
A list of modules to consider standard-library, in addition to those
@ -3213,6 +3231,24 @@ split-on-trailing-comma = false
---
#### [`variables`](#variables)
An override list of tokens to always recognize as a var
for `order-by-type` regardless of casing.
**Default value**: `[]`
**Type**: `Vec<String>`
**Example usage**:
```toml
[tool.ruff.isort]
variables = ["VAR"]
```
---
### `mccabe`
#### [`max-complexity`](#max-complexity)

View File

@ -0,0 +1,2 @@
from sklearn.svm import XYZ, func, variable, Const, Klass, constant
from subprocess import First, var, func, Class, konst, A_constant, Last, STDOUT

View File

@ -0,0 +1,2 @@
from sklearn.svm import VAR, Class, MyVar, CONST, abc
from subprocess import utils, var_ABC, Variable, Klass, CONSTANT, exe

View File

@ -782,6 +782,16 @@
"null"
]
},
"constants": {
"description": "An override list of tokens to always recognize as a CONSTANT for `order-by-type` regardless of casing.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"extra-standard-library": {
"description": "A list of modules to consider standard-library, in addition to those known to Ruff in advance.",
"type": [
@ -877,6 +887,16 @@
"boolean",
"null"
]
},
"variables": {
"description": "An override list of tokens to always recognize as a var for `order-by-type` regardless of casing.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"additionalProperties": false

View File

@ -389,6 +389,8 @@ fn order_imports<'a>(
order_by_type: bool,
relative_imports_order: RelatveImportsOrder,
classes: &'a BTreeSet<String>,
constants: &'a BTreeSet<String>,
variables: &'a BTreeSet<String>,
) -> OrderedImportBlock<'a> {
let mut ordered = OrderedImportBlock::default();
@ -468,7 +470,14 @@ fn order_imports<'a>(
aliases
.into_iter()
.sorted_by(|(alias1, _), (alias2, _)| {
cmp_members(alias1, alias2, order_by_type, classes)
cmp_members(
alias1,
alias2,
order_by_type,
classes,
constants,
variables,
)
})
.collect::<Vec<(AliasData, CommentSet)>>(),
)
@ -480,9 +489,14 @@ fn order_imports<'a>(
(None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater,
(Some((alias1, _)), Some((alias2, _))) => {
cmp_members(alias1, alias2, order_by_type, classes)
}
(Some((alias1, _)), Some((alias2, _))) => cmp_members(
alias1,
alias2,
order_by_type,
classes,
constants,
variables,
),
},
)
},
@ -559,6 +573,8 @@ pub fn format_imports(
single_line_exclusions: &BTreeSet<String>,
split_on_trailing_comma: bool,
classes: &BTreeSet<String>,
constants: &BTreeSet<String>,
variables: &BTreeSet<String>,
) -> String {
let trailer = &block.trailer;
let block = annotate_imports(&block.imports, comments, locator, split_on_trailing_comma);
@ -581,8 +597,14 @@ pub fn format_imports(
// Generate replacement source code.
let mut is_first_block = true;
for import_block in block_by_type.into_values() {
let mut imports =
order_imports(import_block, order_by_type, relative_imports_order, classes);
let mut imports = order_imports(
import_block,
order_by_type,
relative_imports_order,
classes,
constants,
variables,
);
if force_single_line {
imports = force_single_line_imports(imports, single_line_exclusions);
@ -702,6 +724,8 @@ mod tests {
#[test_case(Path::new("trailing_suffix.py"))]
#[test_case(Path::new("type_comments.py"))]
#[test_case(Path::new("order_by_type_with_custom_classes.py"))]
#[test_case(Path::new("order_by_type_with_custom_constants.py"))]
#[test_case(Path::new("order_by_type_with_custom_variables.py"))]
fn default(path: &Path) -> Result<()> {
let snapshot = format!("{}", path.to_string_lossy());
let diagnostics = test_path(
@ -852,6 +876,68 @@ mod tests {
Ok(())
}
#[test_case(Path::new("order_by_type_with_custom_constants.py"))]
fn order_by_type_with_custom_constants(path: &Path) -> Result<()> {
let snapshot = format!(
"order_by_type_with_custom_constants_{}",
path.to_string_lossy()
);
let mut diagnostics = test_path(
Path::new("./resources/test/fixtures/isort")
.join(path)
.as_path(),
&Settings {
isort: super::settings::Settings {
order_by_type: true,
constants: BTreeSet::from([
"Const".to_string(),
"constant".to_string(),
"First".to_string(),
"Last".to_string(),
"A_constant".to_string(),
"konst".to_string(),
]),
..super::settings::Settings::default()
},
src: vec![Path::new("resources/test/fixtures/isort").to_path_buf()],
..Settings::for_rule(RuleCode::I001)
},
)?;
diagnostics.sort_by_key(|diagnostic| diagnostic.location);
insta::assert_yaml_snapshot!(snapshot, diagnostics);
Ok(())
}
#[test_case(Path::new("order_by_type_with_custom_variables.py"))]
fn order_by_type_with_custom_variables(path: &Path) -> Result<()> {
let snapshot = format!(
"order_by_type_with_custom_variables_{}",
path.to_string_lossy()
);
let mut diagnostics = test_path(
Path::new("./resources/test/fixtures/isort")
.join(path)
.as_path(),
&Settings {
isort: super::settings::Settings {
order_by_type: true,
variables: BTreeSet::from([
"VAR".to_string(),
"Variable".to_string(),
"MyVar".to_string(),
"var_ABC".to_string(),
]),
..super::settings::Settings::default()
},
src: vec![Path::new("resources/test/fixtures/isort").to_path_buf()],
..Settings::for_rule(RuleCode::I001)
},
)?;
diagnostics.sort_by_key(|diagnostic| diagnostic.location);
insta::assert_yaml_snapshot!(snapshot, diagnostics);
Ok(())
}
#[test_case(Path::new("force_sort_within_sections.py"))]
fn force_sort_within_sections(path: &Path) -> Result<()> {
let snapshot = format!("force_sort_within_sections_{}", path.to_string_lossy());

View File

@ -86,6 +86,8 @@ pub fn organize_imports(
&settings.isort.single_line_exclusions,
settings.isort.split_on_trailing_comma,
&settings.isort.classes,
&settings.isort.constants,
&settings.isort.variables,
);
// Expand the span the entire range, including leading and trailing space.

View File

@ -181,6 +181,26 @@ pub struct Options {
/// An override list of tokens to always recognize as a Class for
/// `order-by-type` regardless of casing.
pub classes: Option<Vec<String>>,
#[option(
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
constants = ["constant"]
"#
)]
/// An override list of tokens to always recognize as a CONSTANT
/// for `order-by-type` regardless of casing.
pub constants: Option<Vec<String>>,
#[option(
default = r#"[]"#,
value_type = "Vec<String>",
example = r#"
variables = ["VAR"]
"#
)]
/// An override list of tokens to always recognize as a var
/// for `order-by-type` regardless of casing.
pub variables: Option<Vec<String>>,
}
#[derive(Debug, Hash)]
@ -199,6 +219,8 @@ pub struct Settings {
pub single_line_exclusions: BTreeSet<String>,
pub split_on_trailing_comma: bool,
pub classes: BTreeSet<String>,
pub constants: BTreeSet<String>,
pub variables: BTreeSet<String>,
}
impl Default for Settings {
@ -217,6 +239,8 @@ impl Default for Settings {
single_line_exclusions: BTreeSet::new(),
split_on_trailing_comma: true,
classes: BTreeSet::new(),
constants: BTreeSet::new(),
variables: BTreeSet::new(),
}
}
}
@ -241,6 +265,8 @@ impl From<Options> for Settings {
),
split_on_trailing_comma: options.split_on_trailing_comma.unwrap_or(true),
classes: BTreeSet::from_iter(options.classes.unwrap_or_default()),
constants: BTreeSet::from_iter(options.constants.unwrap_or_default()),
variables: BTreeSet::from_iter(options.variables.unwrap_or_default()),
}
}
}
@ -261,6 +287,8 @@ impl From<Settings> for Options {
single_line_exclusions: Some(settings.single_line_exclusions.into_iter().collect()),
split_on_trailing_comma: Some(settings.split_on_trailing_comma),
classes: Some(settings.classes.into_iter().collect()),
constants: Some(settings.constants.into_iter().collect()),
variables: Some(settings.variables.into_iter().collect()),
}
}
}

View File

@ -0,0 +1,22 @@
---
source: src/rules/isort/mod.rs
expression: diagnostics
---
- kind:
UnsortedImports: ~
location:
row: 1
column: 0
end_location:
row: 3
column: 0
fix:
content: "from subprocess import STDOUT, A_constant, Class, First, Last, func, konst, var\n\nfrom sklearn.svm import XYZ, Const, Klass, constant, func, variable\n"
location:
row: 1
column: 0
end_location:
row: 3
column: 0
parent: ~

View File

@ -0,0 +1,22 @@
---
source: src/rules/isort/mod.rs
expression: diagnostics
---
- kind:
UnsortedImports: ~
location:
row: 1
column: 0
end_location:
row: 3
column: 0
fix:
content: "from subprocess import A_constant, First, konst, Last, STDOUT, Class, func, var\n\nfrom sklearn.svm import Const, constant, XYZ, Klass, func, variable\n"
location:
row: 1
column: 0
end_location:
row: 3
column: 0
parent: ~

View File

@ -0,0 +1,22 @@
---
source: src/rules/isort/mod.rs
expression: diagnostics
---
- kind:
UnsortedImports: ~
location:
row: 1
column: 0
end_location:
row: 3
column: 0
fix:
content: "from subprocess import CONSTANT, Klass, Variable, exe, utils, var_ABC\n\nfrom sklearn.svm import CONST, VAR, Class, MyVar, abc\n"
location:
row: 1
column: 0
end_location:
row: 3
column: 0
parent: ~

View File

@ -0,0 +1,22 @@
---
source: src/rules/isort/mod.rs
expression: diagnostics
---
- kind:
UnsortedImports: ~
location:
row: 1
column: 0
end_location:
row: 3
column: 0
fix:
content: "from subprocess import CONSTANT, Klass, exe, utils, var_ABC, Variable\n\nfrom sklearn.svm import CONST, Class, abc, MyVar, VAR\n"
location:
row: 1
column: 0
end_location:
row: 3
column: 0
parent: ~

View File

@ -14,10 +14,21 @@ pub enum Prefix {
Variables,
}
fn prefix(name: &str, classes: &BTreeSet<String>) -> Prefix {
if classes.contains(name) {
fn prefix(
name: &str,
classes: &BTreeSet<String>,
constants: &BTreeSet<String>,
variables: &BTreeSet<String>,
) -> Prefix {
if constants.contains(name) {
// Ex) `CONSTANT`
Prefix::Constants
} else if classes.contains(name) {
// Ex) `CLASS`
Prefix::Classes
} else if variables.contains(name) {
// Ex) `variable`
Prefix::Variables
} else if name.len() > 1 && string::is_upper(name) {
// Ex) `CONSTANT`
Prefix::Constants
@ -48,10 +59,12 @@ pub fn cmp_members(
alias2: &AliasData,
order_by_type: bool,
classes: &BTreeSet<String>,
constants: &BTreeSet<String>,
variables: &BTreeSet<String>,
) -> Ordering {
if order_by_type {
prefix(alias1.name, classes)
.cmp(&prefix(alias2.name, classes))
prefix(alias1.name, classes, constants, variables)
.cmp(&prefix(alias2.name, classes, constants, variables))
.then_with(|| cmp_modules(alias1, alias2))
} else {
cmp_modules(alias1, alias2)