Skip to content

Commit

Permalink
Add PYI024 for flake8-pyi plugin (#4756)
Browse files Browse the repository at this point in the history
  • Loading branch information
qdegraaf authored and konstin committed Jun 13, 2023
1 parent fb2f873 commit 6cdc947
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 0 deletions.
9 changes: 9 additions & 0 deletions crates/ruff/resources/test/fixtures/flake8_pyi/PYI024.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import collections

person: collections.namedtuple # OK

from collections import namedtuple

person: namedtuple # OK

person = namedtuple("Person", ["name", "age"]) # OK
11 changes: 11 additions & 0 deletions crates/ruff/resources/test/fixtures/flake8_pyi/PYI024.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import collections

person: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"

from collections import namedtuple

person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"

person = namedtuple(
"Person", ["name", "age"]
) # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
10 changes: 10 additions & 0 deletions crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,11 @@ where
if self.enabled(Rule::NumpyDeprecatedTypeAlias) {
numpy::rules::deprecated_type_alias(self, expr);
}
if self.is_stub {
if self.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(self, expr);
}
}

// Ex) List[...]
if self.any_enabled(&[
Expand Down Expand Up @@ -2424,6 +2429,11 @@ where
if self.enabled(Rule::PrivateMemberAccess) {
flake8_self::rules::private_member_access(self, expr);
}
if self.is_stub {
if self.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(self, expr);
}
}
pandas_vet::rules::attr(self, attr, value, expr);
}
Expr::Call(ast::ExprCall {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Pyi, "016") => (RuleGroup::Unspecified, Rule::DuplicateUnionMember),
(Flake8Pyi, "020") => (RuleGroup::Unspecified, Rule::QuotedAnnotationInStub),
(Flake8Pyi, "021") => (RuleGroup::Unspecified, Rule::DocstringInStub),
(Flake8Pyi, "024") => (RuleGroup::Unspecified, Rule::CollectionsNamedTuple),
(Flake8Pyi, "032") => (RuleGroup::Unspecified, Rule::AnyEqNeAnnotation),
(Flake8Pyi, "033") => (RuleGroup::Unspecified, Rule::TypeCommentInStub),
(Flake8Pyi, "042") => (RuleGroup::Unspecified, Rule::SnakeCaseTypeAlias),
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ ruff_macros::register_rules!(
rules::flake8_pyi::rules::IterMethodReturnIterable,
rules::flake8_pyi::rules::DuplicateUnionMember,
rules::flake8_pyi::rules::EllipsisInNonEmptyClassBody,
rules::flake8_pyi::rules::CollectionsNamedTuple,
rules::flake8_pyi::rules::NonEmptyStubBody,
rules::flake8_pyi::rules::PassInClassBody,
rules::flake8_pyi::rules::PassStatementStubBody,
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff/src/rules/flake8_pyi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ mod tests {
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))]
#[test_case(Rule::EllipsisInNonEmptyClassBody, Path::new("PYI013.py"))]
#[test_case(Rule::EllipsisInNonEmptyClassBody, Path::new("PYI013.pyi"))]
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.py"))]
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.pyi"))]
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.py"))]
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.pyi"))]
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.py"))]
Expand Down
64 changes: 64 additions & 0 deletions crates/ruff/src/rules/flake8_pyi/rules/collections_named_tuple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use rustpython_parser::ast::Expr;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::prelude::Ranged;

use crate::checkers::ast::Checker;

/// ## What it does
/// Checks for uses of `collections.namedtuple` in stub files.
///
/// ## Why is this bad?
/// `typing.NamedTuple` is the "typed version" of `collections.namedtuple`.
///
/// The class generated by subclassing `typing.NamedTuple` is equivalent to
/// `collections.namedtuple`, with the exception that `typing.NamedTuple`
/// includes an `__annotations__` attribute, which allows type checkers to
/// infer the types of the fields.
///
/// ## Example
/// ```python
/// from collections import namedtuple
///
///
/// person = namedtuple("Person", ["name", "age"])
/// ```
///
/// Use instead:
/// ```python
/// from typing import NamedTuple
///
///
/// class Person(NamedTuple):
/// name: str
/// age: int
/// ```
#[violation]
pub struct CollectionsNamedTuple;

impl Violation for CollectionsNamedTuple {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `typing.NamedTuple` instead of `collections.namedtuple`")
}

fn autofix_title(&self) -> Option<String> {
Some(format!("Replace with `typing.NamedTuple`"))
}
}

/// PYI024
pub(crate) fn collections_named_tuple(checker: &mut Checker, expr: &Expr) {
if checker
.semantic_model()
.resolve_call_path(expr)
.map_or(false, |call_path| {
matches!(call_path.as_slice(), ["collections", "namedtuple"])
})
{
checker
.diagnostics
.push(Diagnostic::new(CollectionsNamedTuple, expr.range()));
}
}
2 changes: 2 additions & 0 deletions crates/ruff/src/rules/flake8_pyi/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub(crate) use any_eq_ne_annotation::{any_eq_ne_annotation, AnyEqNeAnnotation};
pub(crate) use bad_version_info_comparison::{
bad_version_info_comparison, BadVersionInfoComparison,
};
pub(crate) use collections_named_tuple::{collections_named_tuple, CollectionsNamedTuple};
pub(crate) use docstring_in_stubs::{docstring_in_stubs, DocstringInStub};
pub(crate) use duplicate_union_member::{duplicate_union_member, DuplicateUnionMember};
pub(crate) use ellipsis_in_non_empty_class_body::{
Expand Down Expand Up @@ -33,6 +34,7 @@ pub(crate) use unrecognized_platform::{

mod any_eq_ne_annotation;
mod bad_version_info_comparison;
mod collections_named_tuple;
mod docstring_in_stubs;
mod duplicate_union_member;
mod ellipsis_in_non_empty_class_body;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI024.pyi:3:9: PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple`
|
3 | import collections
4 |
5 | person: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
| ^^^^^^^^^^^^^^^^^^^^^^ PYI024
6 |
7 | from collections import namedtuple
|
= help: Replace with `typing.NamedTuple`

PYI024.pyi:7:9: PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple`
|
7 | from collections import namedtuple
8 |
9 | person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
| ^^^^^^^^^^ PYI024
10 |
11 | person = namedtuple(
|
= help: Replace with `typing.NamedTuple`

PYI024.pyi:9:10: PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple`
|
9 | person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
10 |
11 | person = namedtuple(
| ^^^^^^^^^^ PYI024
12 | "Person", ["name", "age"]
13 | ) # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
= help: Replace with `typing.NamedTuple`


1 change: 1 addition & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6cdc947

Please sign in to comment.