mirror of https://github.com/astral-sh/ruff
Implement F407
This commit is contained in:
parent
549732b1da
commit
27ba8900eb
|
|
@ -124,7 +124,7 @@ ruff's goal is to achieve feature-parity with Flake8 when used (1) without any p
|
||||||
stylistic checks; limiting to Python 3 obviates the need for certain compatibility checks.)
|
stylistic checks; limiting to Python 3 obviates the need for certain compatibility checks.)
|
||||||
|
|
||||||
Under those conditions, Flake8 implements about 58 rules, give or take. At time of writing, ruff
|
Under those conditions, Flake8 implements about 58 rules, give or take. At time of writing, ruff
|
||||||
implements 30 rules. (Note that these 30 rules likely cover a disproportionate share of errors:
|
implements 31 rules. (Note that these 31 rules likely cover a disproportionate share of errors:
|
||||||
unused imports, undefined variables, etc.)
|
unused imports, undefined variables, etc.)
|
||||||
|
|
||||||
Of the unimplemented rules, ruff is missing:
|
Of the unimplemented rules, ruff is missing:
|
||||||
|
|
@ -158,6 +158,7 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
|
||||||
| F401 | UnusedImport | `...` imported but unused |
|
| F401 | UnusedImport | `...` imported but unused |
|
||||||
| F403 | ImportStarUsage | Unable to detect undefined names |
|
| F403 | ImportStarUsage | Unable to detect undefined names |
|
||||||
| F404 | LateFutureImport | from __future__ imports must occur at the beginning of the file |
|
| F404 | LateFutureImport | from __future__ imports must occur at the beginning of the file |
|
||||||
|
| F407 | FutureFeatureNotDefined | future feature '...' is not defined |
|
||||||
| F541 | FStringMissingPlaceholders | f-string without any placeholders |
|
| F541 | FStringMissingPlaceholders | f-string without any placeholders |
|
||||||
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated |
|
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated |
|
||||||
| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated |
|
| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated |
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ fn main() {
|
||||||
CheckKind::DoNotAssignLambda,
|
CheckKind::DoNotAssignLambda,
|
||||||
CheckKind::DuplicateArgumentName,
|
CheckKind::DuplicateArgumentName,
|
||||||
CheckKind::FStringMissingPlaceholders,
|
CheckKind::FStringMissingPlaceholders,
|
||||||
|
CheckKind::FutureFeatureNotDefined("...".to_string()),
|
||||||
CheckKind::IOError("...".to_string()),
|
CheckKind::IOError("...".to_string()),
|
||||||
CheckKind::IfTuple,
|
CheckKind::IfTuple,
|
||||||
CheckKind::ImportStarUsage,
|
CheckKind::ImportStarUsage,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import non_existent_feature
|
||||||
|
|
@ -14,6 +14,7 @@ select = [
|
||||||
"F401",
|
"F401",
|
||||||
"F403",
|
"F403",
|
||||||
"F404",
|
"F404",
|
||||||
|
"F407",
|
||||||
"F541",
|
"F541",
|
||||||
"F601",
|
"F601",
|
||||||
"F602",
|
"F602",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use rustpython_parser::ast::{
|
use rustpython_parser::ast::{
|
||||||
|
|
@ -14,6 +15,7 @@ use crate::ast::{checks, operations, visitor};
|
||||||
use crate::autofix::fixer;
|
use crate::autofix::fixer;
|
||||||
use crate::checks::{Check, CheckCode, CheckKind};
|
use crate::checks::{Check, CheckCode, CheckKind};
|
||||||
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||||
|
use crate::python::future::ALL_FEATURE_NAMES;
|
||||||
use crate::python::typing;
|
use crate::python::typing;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
|
||||||
|
|
@ -391,7 +393,16 @@ where
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.futures_allowed && self.settings.select.contains(&CheckCode::F404)
|
if self.settings.select.contains(&CheckCode::F407)
|
||||||
|
&& !ALL_FEATURE_NAMES.contains(&alias.node.name.deref())
|
||||||
|
{
|
||||||
|
self.checks.push(Check::new(
|
||||||
|
CheckKind::FutureFeatureNotDefined(alias.node.name.to_string()),
|
||||||
|
stmt.location,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.settings.select.contains(&CheckCode::F404) && !self.futures_allowed
|
||||||
{
|
{
|
||||||
self.checks
|
self.checks
|
||||||
.push(Check::new(CheckKind::LateFutureImport, stmt.location));
|
.push(Check::new(CheckKind::LateFutureImport, stmt.location));
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ pub enum CheckCode {
|
||||||
F401,
|
F401,
|
||||||
F403,
|
F403,
|
||||||
F404,
|
F404,
|
||||||
|
F407,
|
||||||
F541,
|
F541,
|
||||||
F601,
|
F601,
|
||||||
F602,
|
F602,
|
||||||
|
|
@ -57,6 +58,7 @@ impl FromStr for CheckCode {
|
||||||
"F401" => Ok(CheckCode::F401),
|
"F401" => Ok(CheckCode::F401),
|
||||||
"F403" => Ok(CheckCode::F403),
|
"F403" => Ok(CheckCode::F403),
|
||||||
"F404" => Ok(CheckCode::F404),
|
"F404" => Ok(CheckCode::F404),
|
||||||
|
"F407" => Ok(CheckCode::F407),
|
||||||
"F541" => Ok(CheckCode::F541),
|
"F541" => Ok(CheckCode::F541),
|
||||||
"F601" => Ok(CheckCode::F601),
|
"F601" => Ok(CheckCode::F601),
|
||||||
"F602" => Ok(CheckCode::F602),
|
"F602" => Ok(CheckCode::F602),
|
||||||
|
|
@ -95,6 +97,7 @@ impl CheckCode {
|
||||||
CheckCode::F401 => "F401",
|
CheckCode::F401 => "F401",
|
||||||
CheckCode::F403 => "F403",
|
CheckCode::F403 => "F403",
|
||||||
CheckCode::F404 => "F404",
|
CheckCode::F404 => "F404",
|
||||||
|
CheckCode::F407 => "F407",
|
||||||
CheckCode::F541 => "F541",
|
CheckCode::F541 => "F541",
|
||||||
CheckCode::F601 => "F601",
|
CheckCode::F601 => "F601",
|
||||||
CheckCode::F602 => "F602",
|
CheckCode::F602 => "F602",
|
||||||
|
|
@ -131,6 +134,7 @@ impl CheckCode {
|
||||||
CheckCode::F401 => &LintSource::AST,
|
CheckCode::F401 => &LintSource::AST,
|
||||||
CheckCode::F403 => &LintSource::AST,
|
CheckCode::F403 => &LintSource::AST,
|
||||||
CheckCode::F404 => &LintSource::AST,
|
CheckCode::F404 => &LintSource::AST,
|
||||||
|
CheckCode::F407 => &LintSource::AST,
|
||||||
CheckCode::F541 => &LintSource::AST,
|
CheckCode::F541 => &LintSource::AST,
|
||||||
CheckCode::F601 => &LintSource::AST,
|
CheckCode::F601 => &LintSource::AST,
|
||||||
CheckCode::F602 => &LintSource::AST,
|
CheckCode::F602 => &LintSource::AST,
|
||||||
|
|
@ -174,6 +178,7 @@ pub enum CheckKind {
|
||||||
DoNotAssignLambda,
|
DoNotAssignLambda,
|
||||||
DuplicateArgumentName,
|
DuplicateArgumentName,
|
||||||
FStringMissingPlaceholders,
|
FStringMissingPlaceholders,
|
||||||
|
FutureFeatureNotDefined(String),
|
||||||
IOError(String),
|
IOError(String),
|
||||||
IfTuple,
|
IfTuple,
|
||||||
ImportStarUsage,
|
ImportStarUsage,
|
||||||
|
|
@ -209,6 +214,7 @@ impl CheckKind {
|
||||||
CheckKind::DefaultExceptNotLast => "DefaultExceptNotLast",
|
CheckKind::DefaultExceptNotLast => "DefaultExceptNotLast",
|
||||||
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
|
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
|
||||||
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
|
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
|
||||||
|
CheckKind::FutureFeatureNotDefined(_) => "FutureFeatureNotDefined",
|
||||||
CheckKind::IOError(_) => "IOError",
|
CheckKind::IOError(_) => "IOError",
|
||||||
CheckKind::IfTuple => "IfTuple",
|
CheckKind::IfTuple => "IfTuple",
|
||||||
CheckKind::ImportStarUsage => "ImportStarUsage",
|
CheckKind::ImportStarUsage => "ImportStarUsage",
|
||||||
|
|
@ -246,6 +252,7 @@ impl CheckKind {
|
||||||
CheckKind::DefaultExceptNotLast => &CheckCode::F707,
|
CheckKind::DefaultExceptNotLast => &CheckCode::F707,
|
||||||
CheckKind::DuplicateArgumentName => &CheckCode::F831,
|
CheckKind::DuplicateArgumentName => &CheckCode::F831,
|
||||||
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
|
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
|
||||||
|
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
|
||||||
CheckKind::IOError(_) => &CheckCode::E902,
|
CheckKind::IOError(_) => &CheckCode::E902,
|
||||||
CheckKind::IfTuple => &CheckCode::F634,
|
CheckKind::IfTuple => &CheckCode::F634,
|
||||||
CheckKind::ImportStarUsage => &CheckCode::F403,
|
CheckKind::ImportStarUsage => &CheckCode::F403,
|
||||||
|
|
@ -290,6 +297,9 @@ impl CheckKind {
|
||||||
CheckKind::FStringMissingPlaceholders => {
|
CheckKind::FStringMissingPlaceholders => {
|
||||||
"f-string without any placeholders".to_string()
|
"f-string without any placeholders".to_string()
|
||||||
}
|
}
|
||||||
|
CheckKind::FutureFeatureNotDefined(name) => {
|
||||||
|
format!("future feature '{name}' is not defined")
|
||||||
|
}
|
||||||
CheckKind::IOError(name) => {
|
CheckKind::IOError(name) => {
|
||||||
format!("No such file or directory: `{name}`")
|
format!("No such file or directory: `{name}`")
|
||||||
}
|
}
|
||||||
|
|
@ -384,6 +394,7 @@ impl CheckKind {
|
||||||
CheckKind::DoNotAssignLambda => false,
|
CheckKind::DoNotAssignLambda => false,
|
||||||
CheckKind::DuplicateArgumentName => false,
|
CheckKind::DuplicateArgumentName => false,
|
||||||
CheckKind::FStringMissingPlaceholders => false,
|
CheckKind::FStringMissingPlaceholders => false,
|
||||||
|
CheckKind::FutureFeatureNotDefined(_) => false,
|
||||||
CheckKind::IOError(_) => false,
|
CheckKind::IOError(_) => false,
|
||||||
CheckKind::IfTuple => false,
|
CheckKind::IfTuple => false,
|
||||||
CheckKind::ImportStarUsage => false,
|
CheckKind::ImportStarUsage => false,
|
||||||
|
|
|
||||||
|
|
@ -535,6 +535,31 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f407() -> Result<()> {
|
||||||
|
let mut actual = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/F407.py"),
|
||||||
|
&settings::Settings {
|
||||||
|
line_length: 88,
|
||||||
|
exclude: vec![],
|
||||||
|
select: BTreeSet::from([CheckCode::F407]),
|
||||||
|
},
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
actual.sort_by_key(|check| check.location);
|
||||||
|
let expected = vec![Check {
|
||||||
|
kind: CheckKind::FutureFeatureNotDefined("non_existent_feature".to_string()),
|
||||||
|
location: Location::new(2, 1),
|
||||||
|
fix: None,
|
||||||
|
}];
|
||||||
|
assert_eq!(actual.len(), expected.len());
|
||||||
|
for i in 0..actual.len() {
|
||||||
|
assert_eq!(actual[i], expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn f541() -> Result<()> {
|
fn f541() -> Result<()> {
|
||||||
let mut actual = check_path(
|
let mut actual = check_path(
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,7 @@ other-attribute = 1
|
||||||
CheckCode::F401,
|
CheckCode::F401,
|
||||||
CheckCode::F403,
|
CheckCode::F403,
|
||||||
CheckCode::F404,
|
CheckCode::F404,
|
||||||
|
CheckCode::F407,
|
||||||
CheckCode::F541,
|
CheckCode::F541,
|
||||||
CheckCode::F601,
|
CheckCode::F601,
|
||||||
CheckCode::F602,
|
CheckCode::F602,
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
|
pub mod future;
|
||||||
pub mod typing;
|
pub mod typing;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
/// A copy of `__future__.all_feature_names`.
|
||||||
|
pub const ALL_FEATURE_NAMES: &[&str] = &[
|
||||||
|
"nested_scopes",
|
||||||
|
"generators",
|
||||||
|
"division",
|
||||||
|
"absolute_import",
|
||||||
|
"with_statement",
|
||||||
|
"print_function",
|
||||||
|
"unicode_literals",
|
||||||
|
"barry_as_FLUFL",
|
||||||
|
"generator_stop",
|
||||||
|
"annotations",
|
||||||
|
];
|
||||||
|
|
@ -55,6 +55,7 @@ impl Settings {
|
||||||
CheckCode::E902,
|
CheckCode::E902,
|
||||||
CheckCode::F401,
|
CheckCode::F401,
|
||||||
CheckCode::F403,
|
CheckCode::F403,
|
||||||
|
CheckCode::F407,
|
||||||
CheckCode::F541,
|
CheckCode::F541,
|
||||||
CheckCode::F601,
|
CheckCode::F601,
|
||||||
CheckCode::F602,
|
CheckCode::F602,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue