mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:24 +00:00
Add flake-pyi PYI033 "Do not use type comments in stubs" (#3302)
This commit is contained in:
parent
98209be8aa
commit
3bcffb5bdd
12 changed files with 279 additions and 4 deletions
33
crates/ruff/resources/test/fixtures/flake8_pyi/PYI033.py
vendored
Normal file
33
crates/ruff/resources/test/fixtures/flake8_pyi/PYI033.py
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import TypeAlias
|
||||
|
||||
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
def func(
|
||||
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
): ...
|
||||
|
||||
class Foo:
|
||||
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
G: TypeAlias = None # type: ignore
|
||||
H: TypeAlias = None # type: ignore[attr-defined]
|
||||
I: TypeAlias = None #type: ignore
|
||||
J: TypeAlias = None # type: ignore
|
||||
K: TypeAlias = None# type: ignore
|
||||
L: TypeAlias = None#type:ignore
|
||||
|
||||
# Whole line commented out # type: int
|
||||
M: TypeAlias = None # type: can't parse me!
|
||||
|
||||
class Bar:
|
||||
N: TypeAlias = None # type: can't parse me either!
|
||||
# This whole line is commented out and indented # type: str
|
33
crates/ruff/resources/test/fixtures/flake8_pyi/PYI033.pyi
vendored
Normal file
33
crates/ruff/resources/test/fixtures/flake8_pyi/PYI033.pyi
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import TypeAlias
|
||||
|
||||
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
def func(
|
||||
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
): ...
|
||||
|
||||
class Foo:
|
||||
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
G: TypeAlias = None # type: ignore
|
||||
H: TypeAlias = None # type: ignore[attr-defined]
|
||||
I: TypeAlias = None #type: ignore
|
||||
J: TypeAlias = None # type: ignore
|
||||
K: TypeAlias = None# type: ignore
|
||||
L: TypeAlias = None#type:ignore
|
||||
|
||||
# Whole line commented out # type: int
|
||||
M: TypeAlias = None # type: can't parse me!
|
||||
|
||||
class Bar:
|
||||
N: TypeAlias = None # type: can't parse me either!
|
||||
# This whole line is commented out and indented # type: str
|
|
@ -7,8 +7,8 @@ use crate::lex::docstring_detection::StateMachine;
|
|||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, pyupgrade,
|
||||
ruff,
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_pyi, flake8_quotes, pycodestyle,
|
||||
pyupgrade, ruff,
|
||||
};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
|
@ -18,6 +18,7 @@ pub fn check_tokens(
|
|||
tokens: &[LexResult],
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
is_interface_definition: bool,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
|
@ -55,6 +56,7 @@ pub fn check_tokens(
|
|||
.enabled(&Rule::TrailingCommaOnBareTupleProhibited)
|
||||
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
|
||||
let enforce_extraneous_parenthesis = settings.rules.enabled(&Rule::ExtraneousParentheses);
|
||||
let enforce_type_comment_in_stub = settings.rules.enabled(&Rule::TypeCommentInStub);
|
||||
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
|
@ -161,5 +163,10 @@ pub fn check_tokens(
|
|||
);
|
||||
}
|
||||
|
||||
// PYI033
|
||||
if enforce_type_comment_in_stub && is_interface_definition {
|
||||
diagnostics.extend(flake8_pyi::rules::type_comment_in_stub(tokens));
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
|
|
@ -511,6 +511,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
|||
(Flake8Pyi, "011") => Rule::TypedArgumentSimpleDefaults,
|
||||
(Flake8Pyi, "014") => Rule::ArgumentSimpleDefaults,
|
||||
(Flake8Pyi, "021") => Rule::DocstringInStub,
|
||||
(Flake8Pyi, "033") => Rule::TypeCommentInStub,
|
||||
|
||||
// flake8-pytest-style
|
||||
(Flake8PytestStyle, "001") => Rule::IncorrectFixtureParenthesesStyle,
|
||||
|
|
|
@ -21,6 +21,7 @@ use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
|
|||
use crate::message::{Message, Source};
|
||||
use crate::noqa::{add_noqa, rule_is_ignored};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::resolver::is_interface_definition_path;
|
||||
use crate::rules::pycodestyle;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
|
@ -83,7 +84,14 @@ pub fn check_path(
|
|||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_tokens())
|
||||
{
|
||||
diagnostics.extend(check_tokens(locator, &tokens, settings, autofix));
|
||||
let is_interface_definition = is_interface_definition_path(path);
|
||||
diagnostics.extend(check_tokens(
|
||||
locator,
|
||||
&tokens,
|
||||
settings,
|
||||
autofix,
|
||||
is_interface_definition,
|
||||
));
|
||||
}
|
||||
|
||||
// Run the filesystem-based rules.
|
||||
|
|
|
@ -485,6 +485,7 @@ ruff_macros::register_rules!(
|
|||
rules::flake8_pyi::rules::DocstringInStub,
|
||||
rules::flake8_pyi::rules::TypedArgumentSimpleDefaults,
|
||||
rules::flake8_pyi::rules::ArgumentSimpleDefaults,
|
||||
rules::flake8_pyi::rules::TypeCommentInStub,
|
||||
// flake8-pytest-style
|
||||
rules::flake8_pytest_style::rules::IncorrectFixtureParenthesesStyle,
|
||||
rules::flake8_pytest_style::rules::FixturePositionalArgs,
|
||||
|
@ -838,7 +839,8 @@ impl Rule {
|
|||
| Rule::MultipleStatementsOnOneLineColon
|
||||
| Rule::UselessSemicolon
|
||||
| Rule::MultipleStatementsOnOneLineSemicolon
|
||||
| Rule::TrailingCommaProhibited => &LintSource::Tokens,
|
||||
| Rule::TrailingCommaProhibited
|
||||
| Rule::TypeCommentInStub => &LintSource::Tokens,
|
||||
Rule::IOError => &LintSource::Io,
|
||||
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
|
||||
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => &LintSource::Filesystem,
|
||||
|
|
|
@ -31,6 +31,8 @@ mod tests {
|
|||
#[test_case(Rule::ArgumentSimpleDefaults, Path::new("PYI014.pyi"))]
|
||||
#[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))]
|
||||
#[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))]
|
||||
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))]
|
||||
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
|
|
@ -7,6 +7,7 @@ pub use simple_defaults::{
|
|||
argument_simple_defaults, typed_argument_simple_defaults, ArgumentSimpleDefaults,
|
||||
TypedArgumentSimpleDefaults,
|
||||
};
|
||||
pub use type_comment_in_stub::{type_comment_in_stub, TypeCommentInStub};
|
||||
pub use unrecognized_platform::{
|
||||
unrecognized_platform, UnrecognizedPlatformCheck, UnrecognizedPlatformName,
|
||||
};
|
||||
|
@ -17,4 +18,5 @@ mod non_empty_stub_body;
|
|||
mod pass_statement_stub_body;
|
||||
mod prefix_type_params;
|
||||
mod simple_defaults;
|
||||
mod type_comment_in_stub;
|
||||
mod unrecognized_platform;
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::Tok;
|
||||
|
||||
use ruff_macros::{define_violation, derive_message_formats};
|
||||
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
use crate::Range;
|
||||
|
||||
define_violation!(
|
||||
/// ## What it does
|
||||
/// Checks for the use of type comments (e.g., `x = 1 # type: int`) in stub
|
||||
/// files.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Stub (`.pyi`) files should use type annotations directly, rather
|
||||
/// than type comments, even if they're intended to support Python 2, since
|
||||
/// stub files are not executed at runtime. The one exception is `# type: ignore`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// x = 1 # type: int
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// x: int = 1
|
||||
/// ```
|
||||
pub struct TypeCommentInStub;
|
||||
);
|
||||
impl Violation for TypeCommentInStub {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Don't use type comments in stub file")
|
||||
}
|
||||
}
|
||||
|
||||
static TYPE_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^#\s*type:\s*([^#]+)(\s*#.*?)?$").unwrap());
|
||||
static TYPE_IGNORE_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^#\s*type:\s*ignore([^#]+)?(\s*#.*?)?$").unwrap());
|
||||
|
||||
/// PYI033
|
||||
pub fn type_comment_in_stub(tokens: &[LexResult]) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = vec![];
|
||||
|
||||
for token in tokens.iter().flatten() {
|
||||
if let (location, Tok::Comment(comment), end_location) = token {
|
||||
if TYPE_COMMENT_REGEX.is_match(comment) && !TYPE_IGNORE_REGEX.is_match(comment) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
TypeCommentInStub,
|
||||
Range {
|
||||
location: *location,
|
||||
end_location: *end_location,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 6
|
||||
column: 21
|
||||
end_location:
|
||||
row: 6
|
||||
column: 127
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 7
|
||||
column: 21
|
||||
end_location:
|
||||
row: 7
|
||||
column: 183
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 8
|
||||
column: 21
|
||||
end_location:
|
||||
row: 8
|
||||
column: 126
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 9
|
||||
column: 21
|
||||
end_location:
|
||||
row: 9
|
||||
column: 132
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 10
|
||||
column: 19
|
||||
end_location:
|
||||
row: 10
|
||||
column: 128
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 11
|
||||
column: 19
|
||||
end_location:
|
||||
row: 11
|
||||
column: 123
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 14
|
||||
column: 11
|
||||
end_location:
|
||||
row: 14
|
||||
column: 128
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 15
|
||||
column: 10
|
||||
end_location:
|
||||
row: 15
|
||||
column: 172
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 19
|
||||
column: 28
|
||||
end_location:
|
||||
row: 19
|
||||
column: 139
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 29
|
||||
column: 21
|
||||
end_location:
|
||||
row: 29
|
||||
column: 44
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TypeCommentInStub: ~
|
||||
location:
|
||||
row: 32
|
||||
column: 25
|
||||
end_location:
|
||||
row: 32
|
||||
column: 55
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
2
ruff.schema.json
generated
2
ruff.schema.json
generated
|
@ -1948,6 +1948,8 @@
|
|||
"PYI014",
|
||||
"PYI02",
|
||||
"PYI021",
|
||||
"PYI03",
|
||||
"PYI033",
|
||||
"Q",
|
||||
"Q0",
|
||||
"Q00",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue