Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40b7c64f7d | ||
|
|
a76c5d1226 | ||
|
|
5aeddeb825 | ||
|
|
5f8294aea4 | ||
|
|
e07d3f6313 |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.159
|
||||
rev: v0.0.160
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.159-dev.0"
|
||||
version = "0.0.160-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1821,7 +1821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.159"
|
||||
version = "0.0.160"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1873,7 +1873,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.159"
|
||||
version = "0.0.160"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.159"
|
||||
version = "0.0.160"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
|
||||
26
README.md
26
README.md
@@ -145,7 +145,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.159
|
||||
rev: v0.0.160
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -1295,7 +1295,7 @@ paths.
|
||||
```toml
|
||||
[tool.ruff]
|
||||
exclude = [".venv"]
|
||||
````
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -1313,7 +1313,7 @@ A list of file patterns to omit from linting, in addition to those specified by
|
||||
[tool.ruff]
|
||||
# In addition to the standard set of exclusions, omit all tests, plus a specific file.
|
||||
extend-exclude = ["tests", "src/bad.py"]
|
||||
````
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -1509,6 +1509,26 @@ dummy-variable-rgx = "^_$"
|
||||
|
||||
---
|
||||
|
||||
#### [`allowed-confusables`](#allowed-confusables)
|
||||
|
||||
A list of allowed "confusable" Unicode characters to ignore when enforcing `RUF001`, `RUF002`,
|
||||
and `RUF003`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<char>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Allow minus-sign (U+2212), greek-small-letter-rho (U+03C1), and greek-small-letter-alpha (U+03B1),
|
||||
# which could be confused for "-", "p", and "*", respectively.
|
||||
allowed-confusables = ["−", "ρ", "∗"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`ignore-init-module-imports`](#ignore-init-module-imports)
|
||||
|
||||
Avoid automatically removing unused imports in `__init__.py` files. Such imports will still be
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.159"
|
||||
version = "0.0.160"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.159"
|
||||
version = "0.0.160"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.159-dev.0"
|
||||
version = "0.0.160-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -243,6 +243,7 @@ mod tests {
|
||||
fn it_converts_empty() -> Result<()> {
|
||||
let actual = convert(&HashMap::from([]), None)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -286,6 +287,7 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -329,6 +331,7 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -372,6 +375,7 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -415,6 +419,7 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -466,6 +471,7 @@ mod tests {
|
||||
Some(vec![Plugin::Flake8Docstrings]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -544,6 +550,7 @@ mod tests {
|
||||
None,
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
|
||||
6
resources/test/fixtures/isort/preserve_import_star.py
vendored
Normal file
6
resources/test/fixtures/isort/preserve_import_star.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from some_other_module import some_class
|
||||
from some_other_module import *
|
||||
# Above
|
||||
from some_module import some_class # Aside
|
||||
# Above
|
||||
from some_module import * # Aside
|
||||
10
resources/test/fixtures/pyproject.toml
vendored
10
resources/test/fixtures/pyproject.toml
vendored
@@ -1,4 +1,5 @@
|
||||
[tool.ruff]
|
||||
allowed-confusables = ["−", "ρ", "∗"]
|
||||
line-length = 88
|
||||
extend-exclude = [
|
||||
"excluded_file.py",
|
||||
@@ -35,13 +36,8 @@ ignore-names = [
|
||||
"longMessage",
|
||||
"maxDiff",
|
||||
]
|
||||
classmethod-decorators = [
|
||||
"classmethod",
|
||||
"pydantic.validator",
|
||||
]
|
||||
staticmethod-decorators = [
|
||||
"staticmethod",
|
||||
]
|
||||
classmethod-decorators = ["classmethod", "pydantic.validator"]
|
||||
staticmethod-decorators = ["staticmethod"]
|
||||
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
ban-relative-imports = "parents"
|
||||
|
||||
7
resources/test/fixtures/ruff/RUF002.py
vendored
7
resources/test/fixtures/ruff/RUF002.py
vendored
@@ -1,7 +0,0 @@
|
||||
x = "𝐁ad string"
|
||||
|
||||
|
||||
def f():
|
||||
"""Here's a docstring with an unusual parenthesis: )"""
|
||||
# And here's a comment with an unusual punctuation mark: ᜵
|
||||
...
|
||||
7
resources/test/fixtures/ruff/RUF003.py
vendored
7
resources/test/fixtures/ruff/RUF003.py
vendored
@@ -1,7 +0,0 @@
|
||||
x = "𝐁ad string"
|
||||
|
||||
|
||||
def f():
|
||||
"""Here's a docstring with an unusual parenthesis: )"""
|
||||
# And here's a comment with an unusual punctuation mark: ᜵
|
||||
...
|
||||
@@ -1,7 +1,14 @@
|
||||
x = "𝐁ad string"
|
||||
y = "−"
|
||||
|
||||
|
||||
def f():
|
||||
"""Here's a docstring with an unusual parenthesis: )"""
|
||||
# And here's a comment with an unusual punctuation mark: ᜵
|
||||
...
|
||||
|
||||
|
||||
def g():
|
||||
"""Here's a docstring with a greek rho: ρ"""
|
||||
# And here's a comment with a greek alpha: ∗
|
||||
...
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.159"
|
||||
version = "0.0.160"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -118,9 +118,9 @@ pub type LocatedCmpop<U = ()> = Located<Cmpop, U>;
|
||||
/// Extract all `Cmpop` operators from a source code snippet, with appropriate
|
||||
/// ranges.
|
||||
///
|
||||
/// RustPython doesn't include line and column information on `Cmpop` nodes.
|
||||
/// (CPython doesn't either.) This method iterates over the token stream and
|
||||
/// re-identifies `Cmpop` nodes, annotating them with valid arnges.
|
||||
/// `RustPython` doesn't include line and column information on `Cmpop` nodes.
|
||||
/// `CPython` doesn't either. This method iterates over the token stream and
|
||||
/// re-identifies `Cmpop` nodes, annotating them with valid ranges.
|
||||
pub fn locate_cmpops(contents: &str) -> Vec<LocatedCmpop> {
|
||||
let mut tok_iter = lexer::make_tokenizer(contents)
|
||||
.flatten()
|
||||
|
||||
@@ -191,7 +191,18 @@ fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) ->
|
||||
} => {
|
||||
// Associate the comments with the first alias (best effort).
|
||||
if let Some(alias) = names.first() {
|
||||
if alias.asname.is_none() || combine_as_imports {
|
||||
if alias.name == "*" {
|
||||
let entry = block
|
||||
.import_from_star
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default();
|
||||
for comment in atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
} else if alias.asname.is_none() || combine_as_imports {
|
||||
let entry = &mut block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
@@ -225,7 +236,18 @@ fn normalize_imports(imports: Vec<AnnotatedImport>, combine_as_imports: bool) ->
|
||||
|
||||
// Create an entry for every alias.
|
||||
for alias in names {
|
||||
if alias.asname.is_none() || combine_as_imports {
|
||||
if alias.name == "*" {
|
||||
let entry = block
|
||||
.import_from_star
|
||||
.entry(ImportFromData { module, level })
|
||||
.or_default();
|
||||
for comment in alias.atop {
|
||||
entry.atop.push(comment.value);
|
||||
}
|
||||
for comment in alias.inline {
|
||||
entry.inline.push(comment.value);
|
||||
}
|
||||
} else if alias.asname.is_none() || combine_as_imports {
|
||||
let entry = block
|
||||
.import_from
|
||||
.entry(ImportFromData { module, level })
|
||||
@@ -323,6 +345,22 @@ fn categorize_imports<'a>(
|
||||
.import_from_as
|
||||
.insert((import_from, alias), comments);
|
||||
}
|
||||
// Categorize `StmtKind::ImportFrom` (with star).
|
||||
for (import_from, comments) in block.import_from_star {
|
||||
let classification = categorize(
|
||||
&import_from.module_base(),
|
||||
import_from.level,
|
||||
src,
|
||||
known_first_party,
|
||||
known_third_party,
|
||||
extra_standard_library,
|
||||
);
|
||||
block_by_type
|
||||
.entry(classification)
|
||||
.or_default()
|
||||
.import_from_star
|
||||
.insert(import_from, comments);
|
||||
}
|
||||
block_by_type
|
||||
}
|
||||
|
||||
@@ -367,6 +405,33 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
|
||||
)
|
||||
}),
|
||||
)
|
||||
.chain(
|
||||
// Include all star imports.
|
||||
block
|
||||
.import_from_star
|
||||
.into_iter()
|
||||
.map(|(import_from, comments)| {
|
||||
(
|
||||
import_from,
|
||||
(
|
||||
CommentSet {
|
||||
atop: comments.atop,
|
||||
inline: vec![],
|
||||
},
|
||||
FxHashMap::from_iter([(
|
||||
AliasData {
|
||||
name: "*",
|
||||
asname: None,
|
||||
},
|
||||
CommentSet {
|
||||
atop: vec![],
|
||||
inline: comments.inline,
|
||||
},
|
||||
)]),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.map(|(import_from, (comments, aliases))| {
|
||||
// Within each `StmtKind::ImportFrom`, sort the members.
|
||||
(
|
||||
@@ -486,6 +551,7 @@ mod tests {
|
||||
#[test_case(Path::new("order_by_type.py"))]
|
||||
#[test_case(Path::new("order_relative_imports_by_level.py"))]
|
||||
#[test_case(Path::new("preserve_comment_order.py"))]
|
||||
#[test_case(Path::new("preserve_import_star.py"))]
|
||||
#[test_case(Path::new("preserve_indentation.py"))]
|
||||
#[test_case(Path::new("reorder_within_section.py"))]
|
||||
#[test_case(Path::new("separate_first_party_imports.py"))]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 0
|
||||
fix:
|
||||
content: "# Above\nfrom some_module import * # Aside\n\n# Above\nfrom some_module import some_class # Aside\nfrom some_other_module import *\nfrom some_other_module import some_class\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 0
|
||||
|
||||
@@ -59,6 +59,9 @@ pub struct ImportBlock<'a> {
|
||||
// Set of (module, level, name, asname), used to track re-exported 'from' imports.
|
||||
// Ex) `from module import member as member`
|
||||
pub import_from_as: FxHashMap<(ImportFromData<'a>, AliasData<'a>), CommentSet<'a>>,
|
||||
// Map from (module, level) to `AliasData`, used to track star imports.
|
||||
// Ex) `from module import *`
|
||||
pub import_from_star: FxHashMap<ImportFromData<'a>, CommentSet<'a>>,
|
||||
}
|
||||
|
||||
type AliasDataWithComments<'a> = (AliasData<'a>, CommentSet<'a>);
|
||||
|
||||
@@ -1623,41 +1623,45 @@ pub fn ambiguous_unicode_character(
|
||||
for current_char in text.chars() {
|
||||
// Search for confusing characters.
|
||||
if let Some(representant) = CONFUSABLES.get(&(current_char as u32)) {
|
||||
if let Some(representant) = char::from_u32(*representant) {
|
||||
let col = if row_offset == 0 {
|
||||
start.column() + col_offset
|
||||
} else {
|
||||
col_offset
|
||||
};
|
||||
let location = Location::new(start.row() + row_offset, col);
|
||||
let end_location = Location::new(location.row(), location.column() + 1);
|
||||
let mut check = Check::new(
|
||||
match context {
|
||||
Context::String => {
|
||||
CheckKind::AmbiguousUnicodeCharacterString(current_char, representant)
|
||||
}
|
||||
Context::Docstring => CheckKind::AmbiguousUnicodeCharacterDocstring(
|
||||
current_char,
|
||||
representant,
|
||||
),
|
||||
Context::Comment => {
|
||||
CheckKind::AmbiguousUnicodeCharacterComment(current_char, representant)
|
||||
}
|
||||
},
|
||||
Range {
|
||||
location,
|
||||
end_location,
|
||||
},
|
||||
);
|
||||
if settings.enabled.contains(check.kind.code()) {
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
representant.to_string(),
|
||||
if !settings.allowed_confusables.contains(¤t_char) {
|
||||
if let Some(representant) = char::from_u32(*representant) {
|
||||
let col = if row_offset == 0 {
|
||||
start.column() + col_offset
|
||||
} else {
|
||||
col_offset
|
||||
};
|
||||
let location = Location::new(start.row() + row_offset, col);
|
||||
let end_location = Location::new(location.row(), location.column() + 1);
|
||||
let mut check = Check::new(
|
||||
match context {
|
||||
Context::String => CheckKind::AmbiguousUnicodeCharacterString(
|
||||
current_char,
|
||||
representant,
|
||||
),
|
||||
Context::Docstring => CheckKind::AmbiguousUnicodeCharacterDocstring(
|
||||
current_char,
|
||||
representant,
|
||||
),
|
||||
Context::Comment => CheckKind::AmbiguousUnicodeCharacterComment(
|
||||
current_char,
|
||||
representant,
|
||||
),
|
||||
},
|
||||
Range {
|
||||
location,
|
||||
end_location,
|
||||
));
|
||||
},
|
||||
);
|
||||
if settings.enabled.contains(check.kind.code()) {
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
representant.to_string(),
|
||||
location,
|
||||
end_location,
|
||||
));
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,30 +4,31 @@ pub mod checks;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
|
||||
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
|
||||
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")]
|
||||
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||
#[test]
|
||||
fn confusables() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/ruff")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
&settings::Settings::for_rule(check_code),
|
||||
Path::new("./resources/test/fixtures/ruff/confusables.py"),
|
||||
&settings::Settings {
|
||||
allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']),
|
||||
..settings::Settings::for_rules(vec![
|
||||
CheckCode::RUF001,
|
||||
CheckCode::RUF002,
|
||||
CheckCode::RUF003,
|
||||
])
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
source: src/rules/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousUnicodeCharacterString:
|
||||
- 𝐁
|
||||
- B
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
end_location:
|
||||
row: 1
|
||||
column: 6
|
||||
fix:
|
||||
content: B
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
end_location:
|
||||
row: 1
|
||||
column: 6
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
source: src/rules/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousUnicodeCharacterDocstring:
|
||||
- )
|
||||
- )
|
||||
location:
|
||||
row: 5
|
||||
column: 55
|
||||
end_location:
|
||||
row: 5
|
||||
column: 56
|
||||
fix:
|
||||
content: )
|
||||
location:
|
||||
row: 5
|
||||
column: 55
|
||||
end_location:
|
||||
row: 5
|
||||
column: 56
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
source: src/rules/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousUnicodeCharacterComment:
|
||||
- ᜵
|
||||
- /
|
||||
location:
|
||||
row: 6
|
||||
column: 61
|
||||
end_location:
|
||||
row: 6
|
||||
column: 62
|
||||
fix:
|
||||
content: /
|
||||
location:
|
||||
row: 6
|
||||
column: 61
|
||||
end_location:
|
||||
row: 6
|
||||
column: 62
|
||||
|
||||
59
src/rules/snapshots/ruff__rules__tests__confusables.snap
Normal file
59
src/rules/snapshots/ruff__rules__tests__confusables.snap
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
source: src/rules/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousUnicodeCharacterString:
|
||||
- 𝐁
|
||||
- B
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
end_location:
|
||||
row: 1
|
||||
column: 6
|
||||
fix:
|
||||
content: B
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
end_location:
|
||||
row: 1
|
||||
column: 6
|
||||
- kind:
|
||||
AmbiguousUnicodeCharacterDocstring:
|
||||
- )
|
||||
- )
|
||||
location:
|
||||
row: 6
|
||||
column: 55
|
||||
end_location:
|
||||
row: 6
|
||||
column: 56
|
||||
fix:
|
||||
content: )
|
||||
location:
|
||||
row: 6
|
||||
column: 55
|
||||
end_location:
|
||||
row: 6
|
||||
column: 56
|
||||
- kind:
|
||||
AmbiguousUnicodeCharacterComment:
|
||||
- ᜵
|
||||
- /
|
||||
location:
|
||||
row: 7
|
||||
column: 61
|
||||
end_location:
|
||||
row: 7
|
||||
column: 62
|
||||
fix:
|
||||
content: /
|
||||
location:
|
||||
row: 7
|
||||
column: 61
|
||||
end_location:
|
||||
row: 7
|
||||
column: 62
|
||||
|
||||
@@ -8,6 +8,7 @@ use anyhow::{anyhow, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use path_absolutize::path_dedot;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::checks_gen::{CheckCodePrefix, CATEGORIES};
|
||||
use crate::settings::pyproject::load_options;
|
||||
@@ -19,6 +20,7 @@ use crate::{
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Configuration {
|
||||
pub allowed_confusables: FxHashSet<char>,
|
||||
pub dummy_variable_rgx: Regex,
|
||||
pub exclude: Vec<FilePattern>,
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
@@ -82,9 +84,12 @@ impl Configuration {
|
||||
) -> Result<Self> {
|
||||
let options = load_options(pyproject)?;
|
||||
Ok(Configuration {
|
||||
allowed_confusables: FxHashSet::from_iter(
|
||||
options.allowed_confusables.unwrap_or_default(),
|
||||
),
|
||||
dummy_variable_rgx: match options.dummy_variable_rgx {
|
||||
Some(pattern) => Regex::new(&pattern)
|
||||
.map_err(|e| anyhow!("Invalid dummy-variable-rgx value: {e}"))?,
|
||||
.map_err(|e| anyhow!("Invalid `dummy-variable-rgx` value: {e}"))?,
|
||||
None => DEFAULT_DUMMY_VARIABLE_RGX.clone(),
|
||||
},
|
||||
src: options.src.map_or_else(
|
||||
|
||||
@@ -28,6 +28,7 @@ pub mod types;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub allowed_confusables: FxHashSet<char>,
|
||||
pub dummy_variable_rgx: Regex,
|
||||
pub enabled: FxHashSet<CheckCode>,
|
||||
pub exclude: GlobSet,
|
||||
@@ -58,6 +59,7 @@ impl Settings {
|
||||
project_root: Option<&PathBuf>,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
allowed_confusables: config.allowed_confusables,
|
||||
dummy_variable_rgx: config.dummy_variable_rgx,
|
||||
enabled: resolve_codes(
|
||||
&config
|
||||
@@ -95,6 +97,7 @@ impl Settings {
|
||||
|
||||
pub fn for_rule(check_code: CheckCode) -> Self {
|
||||
Self {
|
||||
allowed_confusables: FxHashSet::from_iter([]),
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: FxHashSet::from_iter([check_code.clone()]),
|
||||
exclude: GlobSet::empty(),
|
||||
@@ -121,6 +124,7 @@ impl Settings {
|
||||
|
||||
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
|
||||
Self {
|
||||
allowed_confusables: FxHashSet::from_iter([]),
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: FxHashSet::from_iter(check_codes.clone()),
|
||||
exclude: GlobSet::empty(),
|
||||
@@ -149,6 +153,9 @@ impl Settings {
|
||||
impl Hash for Settings {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Add base properties in alphabetical order.
|
||||
for confusable in &self.allowed_confusables {
|
||||
confusable.hash(state);
|
||||
}
|
||||
self.dummy_variable_rgx.as_str().hash(state);
|
||||
for value in &self.enabled {
|
||||
value.hash(state);
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::{
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub allowed_confusables: Option<Vec<char>>,
|
||||
pub dummy_variable_rgx: Option<String>,
|
||||
pub exclude: Option<Vec<String>>,
|
||||
pub extend_exclude: Option<Vec<String>>,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use common_path::common_path_all;
|
||||
use log::debug;
|
||||
use path_absolutize::Absolutize;
|
||||
@@ -82,7 +82,8 @@ pub fn find_project_root(sources: &[PathBuf]) -> Option<PathBuf> {
|
||||
|
||||
pub fn load_options(pyproject: Option<&PathBuf>) -> Result<Options> {
|
||||
if let Some(pyproject) = pyproject {
|
||||
Ok(parse_pyproject_toml(pyproject)?
|
||||
Ok(parse_pyproject_toml(pyproject)
|
||||
.map_err(|err| anyhow!("Failed to parse `{}`: {}", pyproject.to_string_lossy(), err))?
|
||||
.tool
|
||||
.and_then(|tool| tool.ruff)
|
||||
.unwrap_or_default())
|
||||
@@ -133,6 +134,7 @@ mod tests {
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -174,6 +176,7 @@ line-length = 79
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -215,6 +218,7 @@ exclude = ["foo.py"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
allowed_confusables: None,
|
||||
line_length: None,
|
||||
fix: None,
|
||||
exclude: Some(vec!["foo.py".to_string()]),
|
||||
@@ -256,6 +260,7 @@ select = ["E501"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -298,6 +303,7 @@ ignore = ["E501"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
allowed_confusables: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
@@ -374,6 +380,7 @@ other-attribute = 1
|
||||
assert_eq!(
|
||||
config,
|
||||
Options {
|
||||
allowed_confusables: Some(vec!['−', 'ρ', '∗']),
|
||||
line_length: Some(88),
|
||||
fix: None,
|
||||
exclude: None,
|
||||
|
||||
Reference in New Issue
Block a user