[flake8-pyi]: PYI009, PYI010, PYI021 (#3230)
PYI009 and PYI010 are very similar, always use `...` in function and class bodies in stubs. PYI021 bans doc strings in stubs. I think all of these rules should be relatively straightforward to implement auto fixes for but can do that later once we get all the other rules added. rel: https://github.com/charliermarsh/ruff/issues/848
This commit is contained in:
14
crates/ruff/resources/test/fixtures/flake8_pyi/PYI009.py
vendored
Normal file
14
crates/ruff/resources/test/fixtures/flake8_pyi/PYI009.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
def bar():
|
||||
... # OK
|
||||
|
||||
|
||||
def foo():
|
||||
pass # OK, since we're not in a stub file
|
||||
|
||||
|
||||
class Bar:
|
||||
... # OK
|
||||
|
||||
|
||||
class Foo:
|
||||
pass # OK, since we're not in a stub file
|
||||
8
crates/ruff/resources/test/fixtures/flake8_pyi/PYI009.pyi
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_pyi/PYI009.pyi
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
def bar(): ... # OK
|
||||
def foo():
|
||||
pass # ERROR PYI009, since we're in a stub file
|
||||
|
||||
class Bar: ... # OK
|
||||
|
||||
class Foo:
|
||||
pass # ERROR PYI009, since we're in a stub file
|
||||
18
crates/ruff/resources/test/fixtures/flake8_pyi/PYI010.py
vendored
Normal file
18
crates/ruff/resources/test/fixtures/flake8_pyi/PYI010.py
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
def bar():
|
||||
... # OK
|
||||
|
||||
|
||||
def foo():
|
||||
"""foo""" # OK
|
||||
|
||||
|
||||
def buzz():
|
||||
print("buzz") # OK, not in stub file
|
||||
|
||||
|
||||
def foo2():
|
||||
123 # OK, not in a stub file
|
||||
|
||||
|
||||
def bizz():
|
||||
x = 123 # OK, not in a stub file
|
||||
12
crates/ruff/resources/test/fixtures/flake8_pyi/PYI010.pyi
vendored
Normal file
12
crates/ruff/resources/test/fixtures/flake8_pyi/PYI010.pyi
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
def bar(): ... # OK
|
||||
def foo():
|
||||
"""foo""" # OK, strings are handled by another rule
|
||||
|
||||
def buzz():
|
||||
print("buzz") # ERROR PYI010
|
||||
|
||||
def foo2():
|
||||
123 # ERROR PYI010
|
||||
|
||||
def bizz():
|
||||
x = 123 # ERROR PYI010
|
||||
14
crates/ruff/resources/test/fixtures/flake8_pyi/PYI021.py
vendored
Normal file
14
crates/ruff/resources/test/fixtures/flake8_pyi/PYI021.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""foo""" # OK, not in stub
|
||||
|
||||
|
||||
def foo():
|
||||
"""foo""" # OK, doc strings are allowed in non-stubs
|
||||
|
||||
|
||||
class Bar:
|
||||
"""bar""" # OK, doc strings are allowed in non-stubs
|
||||
|
||||
|
||||
def bar():
|
||||
x = 1
|
||||
"""foo""" # OK, not a doc string
|
||||
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI021.pyi
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI021.pyi
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
"""foo""" # ERROR PYI021
|
||||
|
||||
def foo():
|
||||
"""foo""" # ERROR PYI021
|
||||
|
||||
class Bar:
|
||||
"""bar""" # ERROR PYI021
|
||||
|
||||
def bar():
|
||||
x = 1
|
||||
"""foo""" # OK, not a doc string
|
||||
@@ -536,6 +536,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_interface_definition {
|
||||
if self.settings.rules.enabled(&Rule::PassStatementStubBody) {
|
||||
flake8_pyi::rules::pass_statement_stub_body(self, body);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NonEmptyStubBody) {
|
||||
flake8_pyi::rules::non_empty_stub_body(self, body);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::DunderFunctionName) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(
|
||||
self.current_scope(),
|
||||
@@ -864,6 +873,11 @@ where
|
||||
);
|
||||
}
|
||||
}
|
||||
if self.is_interface_definition {
|
||||
if self.settings.rules.enabled(&Rule::PassStatementStubBody) {
|
||||
flake8_pyi::rules::pass_statement_stub_body(self, body);
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
.settings
|
||||
@@ -5346,6 +5360,11 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
overloaded_name = flake8_annotations::helpers::overloaded_name(self, &definition);
|
||||
}
|
||||
if self.is_interface_definition {
|
||||
if self.settings.rules.enabled(&Rule::DocstringInStub) {
|
||||
flake8_pyi::rules::docstring_in_stubs(self, definition.docstring);
|
||||
}
|
||||
}
|
||||
|
||||
// pydocstyle
|
||||
if enforce_docstrings {
|
||||
|
||||
@@ -485,6 +485,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
(Flake8Pyi, "001") => Rule::PrefixTypeParams,
|
||||
(Flake8Pyi, "007") => Rule::UnrecognizedPlatformCheck,
|
||||
(Flake8Pyi, "008") => Rule::UnrecognizedPlatformName,
|
||||
(Flake8Pyi, "009") => Rule::PassStatementStubBody,
|
||||
(Flake8Pyi, "010") => Rule::NonEmptyStubBody,
|
||||
(Flake8Pyi, "021") => Rule::DocstringInStub,
|
||||
|
||||
// flake8-pytest-style
|
||||
(Flake8PytestStyle, "001") => Rule::IncorrectFixtureParenthesesStyle,
|
||||
|
||||
@@ -459,6 +459,9 @@ ruff_macros::register_rules!(
|
||||
rules::flake8_pyi::rules::PrefixTypeParams,
|
||||
rules::flake8_pyi::rules::UnrecognizedPlatformCheck,
|
||||
rules::flake8_pyi::rules::UnrecognizedPlatformName,
|
||||
rules::flake8_pyi::rules::PassStatementStubBody,
|
||||
rules::flake8_pyi::rules::NonEmptyStubBody,
|
||||
rules::flake8_pyi::rules::DocstringInStub,
|
||||
// flake8-pytest-style
|
||||
rules::flake8_pytest_style::rules::IncorrectFixtureParenthesesStyle,
|
||||
rules::flake8_pytest_style::rules::FixturePositionalArgs,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use rustpython_parser::ast::Stmt;
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
use rustpython_parser::ast::{Located, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::registry::Diagnostic;
|
||||
@@ -16,7 +17,7 @@ impl Violation for Assert {
|
||||
}
|
||||
|
||||
/// S101
|
||||
pub fn assert_used(stmt: &Located<StmtKind>) -> Diagnostic {
|
||||
pub fn assert_used(stmt: &Stmt) -> Diagnostic {
|
||||
Diagnostic::new(
|
||||
Assert,
|
||||
Range::new(stmt.location, stmt.location.with_col_offset("assert".len())),
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
use rustpython_parser::ast::{ArgData, Arguments, Expr, Located};
|
||||
use rustpython_parser::ast::{Arg, Arguments, Expr};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
|
||||
use super::super::helpers::{matches_password_name, string_literal};
|
||||
use crate::ast::types::Range;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
use super::super::helpers::{matches_password_name, string_literal};
|
||||
|
||||
define_violation!(
|
||||
pub struct HardcodedPasswordDefault {
|
||||
pub string: String,
|
||||
@@ -19,7 +21,7 @@ impl Violation for HardcodedPasswordDefault {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_password_kwarg(arg: &Located<ArgData>, default: &Expr) -> Option<Diagnostic> {
|
||||
fn check_password_kwarg(arg: &Arg, default: &Expr) -> Option<Diagnostic> {
|
||||
let string = string_literal(default).filter(|string| !string.is_empty())?;
|
||||
let kwarg_name = &arg.node.arg;
|
||||
if !matches_password_name(kwarg_name) {
|
||||
|
||||
@@ -19,6 +19,12 @@ mod tests {
|
||||
#[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.py"))]
|
||||
#[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.pyi"))]
|
||||
#[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.py"))]
|
||||
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.py"))]
|
||||
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.pyi"))]
|
||||
#[test_case(Rule::PassStatementStubBody, Path::new("PYI009.py"))]
|
||||
#[test_case(Rule::PassStatementStubBody, Path::new("PYI009.pyi"))]
|
||||
#[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))]
|
||||
#[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
28
crates/ruff/src/rules/flake8_pyi/rules/docstring_in_stubs.rs
Normal file
28
crates/ruff/src/rules/flake8_pyi/rules/docstring_in_stubs.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct DocstringInStub;
|
||||
);
|
||||
impl Violation for DocstringInStub {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Docstrings should not be included in stubs")
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI021
|
||||
pub fn docstring_in_stubs(checker: &mut Checker, docstring: Option<&Expr>) {
|
||||
if let Some(docstr) = &docstring {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
DocstringInStub,
|
||||
Range::from_located(docstr),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
pub use docstring_in_stubs::{docstring_in_stubs, DocstringInStub};
|
||||
pub use non_empty_stub_body::{non_empty_stub_body, NonEmptyStubBody};
|
||||
pub use pass_statement_stub_body::{pass_statement_stub_body, PassStatementStubBody};
|
||||
pub use prefix_type_params::{prefix_type_params, PrefixTypeParams};
|
||||
pub use unrecognized_platform::{
|
||||
unrecognized_platform, UnrecognizedPlatformCheck, UnrecognizedPlatformName,
|
||||
};
|
||||
|
||||
mod docstring_in_stubs;
|
||||
mod non_empty_stub_body;
|
||||
mod pass_statement_stub_body;
|
||||
mod prefix_type_params;
|
||||
mod unrecognized_platform;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
use rustpython_parser::ast::{Constant, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct NonEmptyStubBody;
|
||||
);
|
||||
impl Violation for NonEmptyStubBody {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Function body must contain only `...`")
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI010
|
||||
pub fn non_empty_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
||||
if body.len() != 1 {
|
||||
return;
|
||||
}
|
||||
if let StmtKind::Expr { value } = &body[0].node {
|
||||
if let ExprKind::Constant { value, .. } = &value.node {
|
||||
if matches!(value, Constant::Ellipsis | Constant::Str(_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
NonEmptyStubBody,
|
||||
Range::from_located(&body[0]),
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
use rustpython_parser::ast::{Stmt, StmtKind};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct PassStatementStubBody;
|
||||
);
|
||||
impl Violation for PassStatementStubBody {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Empty body should contain `...`, not `pass`")
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI009
|
||||
pub fn pass_statement_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
||||
if body.len() != 1 {
|
||||
return;
|
||||
}
|
||||
if matches!(body[0].node, StmtKind::Pass) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
PassStatementStubBody,
|
||||
Range::from_located(&body[0]),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
PassStatementStubBody: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 4
|
||||
end_location:
|
||||
row: 3
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
PassStatementStubBody: ~
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
end_location:
|
||||
row: 8
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
NonEmptyStubBody: ~
|
||||
location:
|
||||
row: 6
|
||||
column: 4
|
||||
end_location:
|
||||
row: 6
|
||||
column: 17
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
NonEmptyStubBody: ~
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
end_location:
|
||||
row: 9
|
||||
column: 7
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
NonEmptyStubBody: ~
|
||||
location:
|
||||
row: 12
|
||||
column: 4
|
||||
end_location:
|
||||
row: 12
|
||||
column: 11
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
DocstringInStub: ~
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 9
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
DocstringInStub: ~
|
||||
location:
|
||||
row: 4
|
||||
column: 4
|
||||
end_location:
|
||||
row: 4
|
||||
column: 13
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
DocstringInStub: ~
|
||||
location:
|
||||
row: 7
|
||||
column: 4
|
||||
end_location:
|
||||
row: 7
|
||||
column: 13
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use rustpython_parser::ast::{Expr, ExprKind};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
use rustpython_parser::ast::{ExprKind, Located};
|
||||
|
||||
use crate::ast::types::{BindingKind, Range};
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -47,12 +48,7 @@ impl Violation for UseOfDotValues {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_attr(
|
||||
checker: &mut Checker,
|
||||
attr: &str,
|
||||
value: &Located<ExprKind>,
|
||||
attr_expr: &Located<ExprKind>,
|
||||
) {
|
||||
pub fn check_attr(checker: &mut Checker, attr: &str, value: &Expr, attr_expr: &Expr) {
|
||||
let rules = &checker.settings.rules;
|
||||
let violation: DiagnosticKind = match attr {
|
||||
"ix" if rules.enabled(&Rule::UseOfDotIx) => UseOfDotIx.into(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use rustpython_parser::ast::{Expr, ExprKind};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
use rustpython_parser::ast::{ExprKind, Located};
|
||||
|
||||
use crate::ast::types::{BindingKind, Range};
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -59,7 +60,7 @@ impl Violation for UseOfDotStack {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_call(checker: &mut Checker, func: &Located<ExprKind>) {
|
||||
pub fn check_call(checker: &mut Checker, func: &Expr) {
|
||||
let rules = &checker.settings.rules;
|
||||
let ExprKind::Attribute { value, attr, .. } = &func.node else {return};
|
||||
let violation: DiagnosticKind = match attr.as_str() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use itertools::Itertools;
|
||||
use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind};
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Located};
|
||||
|
||||
use crate::ast::helpers::compose_call_path;
|
||||
use crate::ast::types::Range;
|
||||
@@ -97,7 +98,7 @@ fn handle_name_or_attribute(
|
||||
}
|
||||
|
||||
/// Handles one block of an except (use a loop if there are multiple blocks)
|
||||
fn handle_except_block(checker: &mut Checker, handler: &Located<ExcepthandlerKind>) {
|
||||
fn handle_except_block(checker: &mut Checker, handler: &Excepthandler) {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
|
||||
let Some(error_handlers) = type_.as_ref() else {
|
||||
return;
|
||||
|
||||
5
ruff.schema.json
generated
5
ruff.schema.json
generated
@@ -1912,6 +1912,11 @@
|
||||
"PYI001",
|
||||
"PYI007",
|
||||
"PYI008",
|
||||
"PYI009",
|
||||
"PYI01",
|
||||
"PYI010",
|
||||
"PYI02",
|
||||
"PYI021",
|
||||
"Q",
|
||||
"Q0",
|
||||
"Q00",
|
||||
|
||||
Reference in New Issue
Block a user