mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-10 05:39:12 +00:00
[flake8-pyi
] Implement PYI025
(#4791)
This commit is contained in:
parent
8d5d34c6d1
commit
6d94aa89e3
11 changed files with 148 additions and 6 deletions
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.py
vendored
Normal file
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.py
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
from collections.abc import Set as AbstractSet # Ok
|
||||
|
||||
|
||||
from collections.abc import Set # Ok
|
||||
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set, # Ok
|
||||
ValuesView
|
||||
)
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set as AbstractSet, # Ok
|
||||
ValuesView
|
||||
)
|
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.pyi
vendored
Normal file
19
crates/ruff/resources/test/fixtures/flake8_pyi/PYI025.pyi
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
from collections.abc import Set as AbstractSet # Ok
|
||||
|
||||
|
||||
from collections.abc import Set # PYI025
|
||||
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set, # PYI025
|
||||
ValuesView
|
||||
)
|
||||
|
||||
from collections.abc import (
|
||||
Container,
|
||||
Sized,
|
||||
Set as AbstractSet,
|
||||
ValuesView # Ok
|
||||
)
|
|
@ -1021,12 +1021,14 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
Stmt::ImportFrom(ast::StmtImportFrom {
|
||||
names,
|
||||
module,
|
||||
level,
|
||||
range: _,
|
||||
}) => {
|
||||
Stmt::ImportFrom(
|
||||
import_from @ ast::StmtImportFrom {
|
||||
names,
|
||||
module,
|
||||
level,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
let module = module.as_deref();
|
||||
let level = level.map(|level| level.to_u32());
|
||||
if self.enabled(Rule::ModuleImportNotAtTopOfFile) {
|
||||
|
@ -1091,6 +1093,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
if self.is_stub {
|
||||
if self.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
|
||||
flake8_pyi::rules::unaliased_collections_abc_set_import(self, import_from);
|
||||
}
|
||||
}
|
||||
for alias in names {
|
||||
if let Some("__future__") = module {
|
||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||
|
|
|
@ -593,6 +593,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Flake8Pyi, "020") => (RuleGroup::Unspecified, Rule::QuotedAnnotationInStub),
|
||||
(Flake8Pyi, "021") => (RuleGroup::Unspecified, Rule::DocstringInStub),
|
||||
(Flake8Pyi, "024") => (RuleGroup::Unspecified, Rule::CollectionsNamedTuple),
|
||||
(Flake8Pyi, "025") => (RuleGroup::Unspecified, Rule::UnaliasedCollectionsAbcSetImport),
|
||||
(Flake8Pyi, "032") => (RuleGroup::Unspecified, Rule::AnyEqNeAnnotation),
|
||||
(Flake8Pyi, "033") => (RuleGroup::Unspecified, Rule::TypeCommentInStub),
|
||||
(Flake8Pyi, "042") => (RuleGroup::Unspecified, Rule::SnakeCaseTypeAlias),
|
||||
|
|
|
@ -529,6 +529,7 @@ ruff_macros::register_rules!(
|
|||
rules::flake8_pyi::rules::TSuffixedTypeAlias,
|
||||
rules::flake8_pyi::rules::TypeCommentInStub,
|
||||
rules::flake8_pyi::rules::TypedArgumentDefaultInStub,
|
||||
rules::flake8_pyi::rules::UnaliasedCollectionsAbcSetImport,
|
||||
rules::flake8_pyi::rules::UnannotatedAssignmentInStub,
|
||||
rules::flake8_pyi::rules::UnprefixedTypeParam,
|
||||
rules::flake8_pyi::rules::UnrecognizedPlatformCheck,
|
||||
|
|
|
@ -48,6 +48,8 @@ mod tests {
|
|||
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))]
|
||||
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))]
|
||||
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.pyi"))]
|
||||
#[test_case(Rule::UnaliasedCollectionsAbcSetImport, Path::new("PYI025.py"))]
|
||||
#[test_case(Rule::UnaliasedCollectionsAbcSetImport, Path::new("PYI025.pyi"))]
|
||||
#[test_case(Rule::UnannotatedAssignmentInStub, Path::new("PYI052.py"))]
|
||||
#[test_case(Rule::UnannotatedAssignmentInStub, Path::new("PYI052.pyi"))]
|
||||
#[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.py"))]
|
||||
|
|
|
@ -28,6 +28,9 @@ pub(crate) use type_alias_naming::{
|
|||
snake_case_type_alias, t_suffixed_type_alias, SnakeCaseTypeAlias, TSuffixedTypeAlias,
|
||||
};
|
||||
pub(crate) use type_comment_in_stub::{type_comment_in_stub, TypeCommentInStub};
|
||||
pub(crate) use unaliased_collections_abc_set_import::{
|
||||
unaliased_collections_abc_set_import, UnaliasedCollectionsAbcSetImport,
|
||||
};
|
||||
pub(crate) use unrecognized_platform::{
|
||||
unrecognized_platform, UnrecognizedPlatformCheck, UnrecognizedPlatformName,
|
||||
};
|
||||
|
@ -48,4 +51,5 @@ mod simple_defaults;
|
|||
mod stub_body_multiple_statements;
|
||||
mod type_alias_naming;
|
||||
mod type_comment_in_stub;
|
||||
mod unaliased_collections_abc_set_import;
|
||||
mod unrecognized_platform;
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
use rustpython_parser::ast::StmtImportFrom;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `from collections.abc import Set` imports that do not alias
|
||||
/// `Set` to `AbstractSet`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// The `Set` type in `collections.abc` is an abstract base class for set-like types.
|
||||
/// It is easily confused with, and not equivalent to, the `set` builtin.
|
||||
///
|
||||
/// To avoid confusion, `Set` should be aliased to `AbstractSet` when imported. This
|
||||
/// makes it clear that the imported type is an abstract base class, and not the
|
||||
/// `set` builtin.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from collections.abc import Set
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from collections.abc import Set as AbstractSet
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct UnaliasedCollectionsAbcSetImport;
|
||||
|
||||
impl Violation for UnaliasedCollectionsAbcSetImport {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin"
|
||||
)
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
Some(format!("Alias `Set` to `AbstractSet`"))
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI025
|
||||
pub(crate) fn unaliased_collections_abc_set_import(checker: &mut Checker, stmt: &StmtImportFrom) {
|
||||
let Some(module_id) = &stmt.module else {
|
||||
return;
|
||||
};
|
||||
if module_id.as_str() != "collections.abc" {
|
||||
return;
|
||||
}
|
||||
|
||||
for name in &stmt.names {
|
||||
if name.name.as_str() == "Set" && name.asname.is_none() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
UnaliasedCollectionsAbcSetImport,
|
||||
name.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI025.pyi:4:29: PYI025 Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin
|
||||
|
|
||||
4 | from collections.abc import Set # PYI025
|
||||
| ^^^ PYI025
|
||||
|
|
||||
= help: Alias `Set` to `AbstractSet`
|
||||
|
||||
PYI025.pyi:10:5: PYI025 Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin
|
||||
|
|
||||
10 | Container,
|
||||
11 | Sized,
|
||||
12 | Set, # PYI025
|
||||
| ^^^ PYI025
|
||||
13 | ValuesView
|
||||
14 | )
|
||||
|
|
||||
= help: Alias `Set` to `AbstractSet`
|
||||
|
||||
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -2240,6 +2240,7 @@
|
|||
"PYI020",
|
||||
"PYI021",
|
||||
"PYI024",
|
||||
"PYI025",
|
||||
"PYI03",
|
||||
"PYI032",
|
||||
"PYI033",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue