mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Implement F406 (#172)
This commit is contained in:
parent
2ca3f35bd1
commit
dfba1416b2
9 changed files with 88 additions and 14 deletions
11
README.md
11
README.md
|
@ -123,15 +123,15 @@ ruff's goal is to achieve feature-parity with Flake8 when used (1) without any p
|
|||
(2) alongside Black, and (3) on Python 3 code. (Using Black obviates the need for many of Flake8's
|
||||
stylistic checks; limiting to Python 3 obviates the need for certain compatibility checks.)
|
||||
|
||||
Under those conditions, Flake8 implements about 61 rules, give or take. At time of writing, ruff
|
||||
implements 36 rules. (Note that these 36 rules likely cover a disproportionate share of errors:
|
||||
Under those conditions, Flake8 implements about 60 rules, give or take. At time of writing, ruff
|
||||
implements 37 rules. (Note that these 37 rules likely cover a disproportionate share of errors:
|
||||
unused imports, undefined variables, etc.)
|
||||
|
||||
The 25 unimplemented rules are tracked in #170, and include:
|
||||
The 23 unimplemented rules are tracked in #170, and include:
|
||||
|
||||
- 14 rules related to string `.format` calls.
|
||||
- 3 rules related to parsing and syntax errors.
|
||||
- 8 logical rules.
|
||||
- 6 logical rules.
|
||||
|
||||
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
|
||||
|
@ -156,8 +156,9 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
|
|||
| E743 | AmbiguousFunctionName | ambiguous function name '...' |
|
||||
| E902 | IOError | No such file or directory: `...` |
|
||||
| F401 | UnusedImport | `...` imported but unused |
|
||||
| F403 | ImportStarUsage | Unable to detect undefined names |
|
||||
| F403 | ImportStarUsage | `from ... import *` used; unable to detect undefined names |
|
||||
| F404 | LateFutureImport | from __future__ imports must occur at the beginning of the file |
|
||||
| F406 | ImportStarNotPermitted | `from ... import *` only allowed at module level |
|
||||
| F407 | FutureFeatureNotDefined | future feature '...' is not defined |
|
||||
| F541 | FStringMissingPlaceholders | f-string without any placeholders |
|
||||
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated |
|
||||
|
|
|
@ -17,7 +17,8 @@ fn main() {
|
|||
CheckKind::FutureFeatureNotDefined("...".to_string()),
|
||||
CheckKind::IOError("...".to_string()),
|
||||
CheckKind::IfTuple,
|
||||
CheckKind::ImportStarUsage,
|
||||
CheckKind::ImportStarNotPermitted("...".to_string()),
|
||||
CheckKind::ImportStarUsage("...".to_string()),
|
||||
CheckKind::LateFutureImport,
|
||||
CheckKind::LineTooLong(89, 88),
|
||||
CheckKind::ModuleImportNotAtTopOfFile,
|
||||
|
|
9
resources/test/fixtures/F406.py
vendored
Normal file
9
resources/test/fixtures/F406.py
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
from F634 import *
|
||||
|
||||
|
||||
def f():
|
||||
from F634 import *
|
||||
|
||||
|
||||
class F:
|
||||
from F634 import *
|
1
resources/test/fixtures/pyproject.toml
vendored
1
resources/test/fixtures/pyproject.toml
vendored
|
@ -17,6 +17,7 @@ select = [
|
|||
"F401",
|
||||
"F403",
|
||||
"F404",
|
||||
"F406",
|
||||
"F407",
|
||||
"F541",
|
||||
"F601",
|
||||
|
|
|
@ -448,8 +448,25 @@ where
|
|||
);
|
||||
|
||||
if self.settings.select.contains(&CheckCode::F403) {
|
||||
self.checks
|
||||
.push(Check::new(CheckKind::ImportStarUsage, stmt.location));
|
||||
self.checks.push(Check::new(
|
||||
CheckKind::ImportStarUsage(
|
||||
module.clone().unwrap_or_else(|| "module".to_string()),
|
||||
),
|
||||
stmt.location,
|
||||
));
|
||||
}
|
||||
|
||||
if self.settings.select.contains(&CheckCode::F406) {
|
||||
let scope = &self.scopes
|
||||
[*(self.scope_stack.last().expect("No current scope found."))];
|
||||
if !matches!(scope.kind, ScopeKind::Module) {
|
||||
self.checks.push(Check::new(
|
||||
CheckKind::ImportStarNotPermitted(
|
||||
module.clone().unwrap_or_else(|| "module".to_string()),
|
||||
),
|
||||
stmt.location,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let binding = Binding {
|
||||
|
|
|
@ -23,6 +23,7 @@ pub enum CheckCode {
|
|||
F401,
|
||||
F403,
|
||||
F404,
|
||||
F406,
|
||||
F407,
|
||||
F541,
|
||||
F601,
|
||||
|
@ -66,6 +67,7 @@ impl FromStr for CheckCode {
|
|||
"F401" => Ok(CheckCode::F401),
|
||||
"F403" => Ok(CheckCode::F403),
|
||||
"F404" => Ok(CheckCode::F404),
|
||||
"F406" => Ok(CheckCode::F406),
|
||||
"F407" => Ok(CheckCode::F407),
|
||||
"F541" => Ok(CheckCode::F541),
|
||||
"F601" => Ok(CheckCode::F601),
|
||||
|
@ -110,6 +112,7 @@ impl CheckCode {
|
|||
CheckCode::F401 => "F401",
|
||||
CheckCode::F403 => "F403",
|
||||
CheckCode::F404 => "F404",
|
||||
CheckCode::F406 => "F406",
|
||||
CheckCode::F407 => "F407",
|
||||
CheckCode::F541 => "F541",
|
||||
CheckCode::F601 => "F601",
|
||||
|
@ -173,7 +176,8 @@ pub enum CheckKind {
|
|||
FutureFeatureNotDefined(String),
|
||||
IOError(String),
|
||||
IfTuple,
|
||||
ImportStarUsage,
|
||||
ImportStarNotPermitted(String),
|
||||
ImportStarUsage(String),
|
||||
LateFutureImport,
|
||||
LineTooLong(usize, usize),
|
||||
ModuleImportNotAtTopOfFile,
|
||||
|
@ -215,7 +219,8 @@ impl CheckKind {
|
|||
CheckKind::FutureFeatureNotDefined(_) => "FutureFeatureNotDefined",
|
||||
CheckKind::IOError(_) => "IOError",
|
||||
CheckKind::IfTuple => "IfTuple",
|
||||
CheckKind::ImportStarUsage => "ImportStarUsage",
|
||||
CheckKind::ImportStarNotPermitted(_) => "ImportStarNotPermitted",
|
||||
CheckKind::ImportStarUsage(_) => "ImportStarUsage",
|
||||
CheckKind::LateFutureImport => "LateFutureImport",
|
||||
CheckKind::LineTooLong(_, _) => "LineTooLong",
|
||||
CheckKind::ModuleImportNotAtTopOfFile => "ModuleImportNotAtTopOfFile",
|
||||
|
@ -259,7 +264,8 @@ impl CheckKind {
|
|||
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
|
||||
CheckKind::IOError(_) => &CheckCode::E902,
|
||||
CheckKind::IfTuple => &CheckCode::F634,
|
||||
CheckKind::ImportStarUsage => &CheckCode::F403,
|
||||
CheckKind::ImportStarNotPermitted(_) => &CheckCode::F406,
|
||||
CheckKind::ImportStarUsage(_) => &CheckCode::F403,
|
||||
CheckKind::LateFutureImport => &CheckCode::F404,
|
||||
CheckKind::LineTooLong(_, _) => &CheckCode::E501,
|
||||
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
|
||||
|
@ -321,7 +327,12 @@ impl CheckKind {
|
|||
format!("No such file or directory: `{name}`")
|
||||
}
|
||||
CheckKind::IfTuple => "If test is a tuple, which is always `True`".to_string(),
|
||||
CheckKind::ImportStarUsage => "Unable to detect undefined names".to_string(),
|
||||
CheckKind::ImportStarNotPermitted(name) => {
|
||||
format!("`from {name} import *` only allowed at module level")
|
||||
}
|
||||
CheckKind::ImportStarUsage(name) => {
|
||||
format!("`from {name} import *` used; unable to detect undefined names")
|
||||
}
|
||||
CheckKind::LateFutureImport => {
|
||||
"from __future__ imports must occur at the beginning of the file".to_string()
|
||||
}
|
||||
|
|
|
@ -598,12 +598,12 @@ mod tests {
|
|||
actual.sort_by_key(|check| check.location);
|
||||
let expected = vec![
|
||||
Check {
|
||||
kind: CheckKind::ImportStarUsage,
|
||||
kind: CheckKind::ImportStarUsage("F634".to_string()),
|
||||
location: Location::new(1, 1),
|
||||
fix: None,
|
||||
},
|
||||
Check {
|
||||
kind: CheckKind::ImportStarUsage,
|
||||
kind: CheckKind::ImportStarUsage("F634".to_string()),
|
||||
location: Location::new(2, 1),
|
||||
fix: None,
|
||||
},
|
||||
|
@ -641,6 +641,38 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f406() -> Result<()> {
|
||||
let mut actual = check_path(
|
||||
Path::new("./resources/test/fixtures/F406.py"),
|
||||
&settings::Settings {
|
||||
line_length: 88,
|
||||
exclude: vec![],
|
||||
select: BTreeSet::from([CheckCode::F406]),
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
actual.sort_by_key(|check| check.location);
|
||||
let expected = vec![
|
||||
Check {
|
||||
kind: CheckKind::ImportStarNotPermitted("F634".to_string()),
|
||||
location: Location::new(5, 5),
|
||||
fix: None,
|
||||
},
|
||||
Check {
|
||||
kind: CheckKind::ImportStarNotPermitted("F634".to_string()),
|
||||
location: Location::new(9, 5),
|
||||
fix: None,
|
||||
},
|
||||
];
|
||||
assert_eq!(actual.len(), expected.len());
|
||||
for i in 0..actual.len() {
|
||||
assert_eq!(actual[i], expected[i]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f407() -> Result<()> {
|
||||
let mut actual = check_path(
|
||||
|
|
|
@ -274,6 +274,7 @@ other-attribute = 1
|
|||
CheckCode::F401,
|
||||
CheckCode::F403,
|
||||
CheckCode::F404,
|
||||
CheckCode::F406,
|
||||
CheckCode::F407,
|
||||
CheckCode::F541,
|
||||
CheckCode::F601,
|
||||
|
|
|
@ -58,6 +58,7 @@ impl Settings {
|
|||
CheckCode::E902,
|
||||
CheckCode::F401,
|
||||
CheckCode::F403,
|
||||
CheckCode::F406,
|
||||
CheckCode::F407,
|
||||
CheckCode::F541,
|
||||
CheckCode::F601,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue