Implement F406 (#172)

This commit is contained in:
Charlie Marsh 2022-09-12 16:47:30 -04:00 committed by GitHub
parent 2ca3f35bd1
commit dfba1416b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 88 additions and 14 deletions

View file

@ -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 |

View file

@ -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
View file

@ -0,0 +1,9 @@
from F634 import *
def f():
from F634 import *
class F:
from F634 import *

View file

@ -17,6 +17,7 @@ select = [
"F401",
"F403",
"F404",
"F406",
"F407",
"F541",
"F601",

View file

@ -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 {

View file

@ -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()
}

View file

@ -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(

View file

@ -274,6 +274,7 @@ other-attribute = 1
CheckCode::F401,
CheckCode::F403,
CheckCode::F404,
CheckCode::F406,
CheckCode::F407,
CheckCode::F541,
CheckCode::F601,

View file

@ -58,6 +58,7 @@ impl Settings {
CheckCode::E902,
CheckCode::F401,
CheckCode::F403,
CheckCode::F406,
CheckCode::F407,
CheckCode::F541,
CheckCode::F601,