mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:53 +00:00
[flake8-pyi] Implement PYI057 (#11486)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
163c374242
commit
7659114eb3
11 changed files with 215 additions and 0 deletions
10
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI057.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI057.py
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import typing
|
||||||
|
import collections.abc
|
||||||
|
import foo
|
||||||
|
from typing import ByteString
|
||||||
|
from collections.abc import ByteString
|
||||||
|
from foo import ByteString
|
||||||
|
|
||||||
|
a: typing.ByteString
|
||||||
|
b: collections.abc.ByteString
|
||||||
|
c: foo.ByteString
|
10
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI057.pyi
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI057.pyi
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import typing
|
||||||
|
import collections.abc
|
||||||
|
import foo
|
||||||
|
from typing import ByteString
|
||||||
|
from collections.abc import ByteString
|
||||||
|
from foo import ByteString
|
||||||
|
|
||||||
|
a: typing.ByteString
|
||||||
|
b: collections.abc.ByteString
|
||||||
|
c: foo.ByteString
|
|
@ -346,6 +346,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::PandasUseOfDotValues) {
|
if checker.enabled(Rule::PandasUseOfDotValues) {
|
||||||
pandas_vet::rules::attr(checker, attribute);
|
pandas_vet::rules::attr(checker, attribute);
|
||||||
}
|
}
|
||||||
|
if checker.enabled(Rule::ByteStringUsage) {
|
||||||
|
flake8_pyi::rules::bytestring_attribute(checker, expr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::Call(
|
Expr::Call(
|
||||||
call @ ast::ExprCall {
|
call @ ast::ExprCall {
|
||||||
|
|
|
@ -1027,6 +1027,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if checker.enabled(Rule::ByteStringUsage) {
|
||||||
|
flake8_pyi::rules::bytestring_import(checker, import_from);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Stmt::Raise(raise @ ast::StmtRaise { exc, .. }) => {
|
Stmt::Raise(raise @ ast::StmtRaise { exc, .. }) => {
|
||||||
if checker.enabled(Rule::RaiseNotImplemented) {
|
if checker.enabled(Rule::RaiseNotImplemented) {
|
||||||
|
|
|
@ -809,6 +809,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Flake8Pyi, "055") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnnecessaryTypeUnion),
|
(Flake8Pyi, "055") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnnecessaryTypeUnion),
|
||||||
(Flake8Pyi, "056") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll),
|
(Flake8Pyi, "056") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll),
|
||||||
(Flake8Pyi, "058") => (RuleGroup::Stable, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod),
|
(Flake8Pyi, "058") => (RuleGroup::Stable, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod),
|
||||||
|
(Flake8Pyi, "057") => (RuleGroup::Preview, rules::flake8_pyi::rules::ByteStringUsage),
|
||||||
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
|
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
|
||||||
(Flake8Pyi, "062") => (RuleGroup::Preview, rules::flake8_pyi::rules::DuplicateLiteralMember),
|
(Flake8Pyi, "062") => (RuleGroup::Preview, rules::flake8_pyi::rules::DuplicateLiteralMember),
|
||||||
(Flake8Pyi, "064") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantFinalLiteral),
|
(Flake8Pyi, "064") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantFinalLiteral),
|
||||||
|
|
|
@ -25,6 +25,8 @@ mod tests {
|
||||||
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
|
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
|
||||||
#[test_case(Rule::BadVersionInfoOrder, Path::new("PYI066.py"))]
|
#[test_case(Rule::BadVersionInfoOrder, Path::new("PYI066.py"))]
|
||||||
#[test_case(Rule::BadVersionInfoOrder, Path::new("PYI066.pyi"))]
|
#[test_case(Rule::BadVersionInfoOrder, Path::new("PYI066.pyi"))]
|
||||||
|
#[test_case(Rule::ByteStringUsage, Path::new("PYI057.py"))]
|
||||||
|
#[test_case(Rule::ByteStringUsage, Path::new("PYI057.pyi"))]
|
||||||
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.py"))]
|
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.py"))]
|
||||||
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.pyi"))]
|
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.pyi"))]
|
||||||
#[test_case(Rule::ComplexAssignmentInStub, Path::new("PYI017.py"))]
|
#[test_case(Rule::ComplexAssignmentInStub, Path::new("PYI017.py"))]
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
use ruff_diagnostics::{Diagnostic, FixAvailability, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
use ruff_python_semantic::Modules;
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for uses of `typing.ByteString` or `collections.abc.ByteString`.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// `ByteString` has been deprecated since Python 3.9 and will be removed in
|
||||||
|
/// Python 3.14. The Python documentation recommends using either
|
||||||
|
/// `collections.abc.Buffer` (or the `typing_extensions` backport
|
||||||
|
/// on Python <3.12) or a union like `bytes | bytearray | memoryview` instead.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// from typing import ByteString
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// from collections.abc import Buffer
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## References
|
||||||
|
/// - [Python documentation: The `ByteString` type](https://docs.python.org/3/library/typing.html#typing.ByteString)
|
||||||
|
#[violation]
|
||||||
|
pub struct ByteStringUsage {
|
||||||
|
origin: ByteStringOrigin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Violation for ByteStringUsage {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::None;
|
||||||
|
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
let ByteStringUsage { origin } = self;
|
||||||
|
format!("Do not use `{origin}.ByteString`, which has unclear semantics and is deprecated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
enum ByteStringOrigin {
|
||||||
|
Typing,
|
||||||
|
CollectionsAbc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ByteStringOrigin {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
Self::Typing => "typing",
|
||||||
|
Self::CollectionsAbc => "collections.abc",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PYI057
|
||||||
|
pub(crate) fn bytestring_attribute(checker: &mut Checker, attribute: &Expr) {
|
||||||
|
let semantic = checker.semantic();
|
||||||
|
if !semantic
|
||||||
|
.seen
|
||||||
|
.intersects(Modules::TYPING | Modules::COLLECTIONS)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(qualified_name) = semantic.resolve_qualified_name(attribute) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let origin = match qualified_name.segments() {
|
||||||
|
["typing", "ByteString"] => ByteStringOrigin::Typing,
|
||||||
|
["collections", "abc", "ByteString"] => ByteStringOrigin::CollectionsAbc,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
ByteStringUsage { origin },
|
||||||
|
attribute.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PYI057
|
||||||
|
pub(crate) fn bytestring_import(checker: &mut Checker, import_from: &ast::StmtImportFrom) {
|
||||||
|
let ast::StmtImportFrom { names, module, .. } = import_from;
|
||||||
|
|
||||||
|
let module_id = match module {
|
||||||
|
Some(module) => module.id.as_str(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let origin = match module_id {
|
||||||
|
"typing" => ByteStringOrigin::Typing,
|
||||||
|
"collections.abc" => ByteStringOrigin::CollectionsAbc,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
for name in names {
|
||||||
|
if name.name.as_str() == "ByteString" {
|
||||||
|
checker
|
||||||
|
.diagnostics
|
||||||
|
.push(Diagnostic::new(ByteStringUsage { origin }, name.range()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
pub(crate) use any_eq_ne_annotation::*;
|
pub(crate) use any_eq_ne_annotation::*;
|
||||||
pub(crate) use bad_generator_return_type::*;
|
pub(crate) use bad_generator_return_type::*;
|
||||||
pub(crate) use bad_version_info_comparison::*;
|
pub(crate) use bad_version_info_comparison::*;
|
||||||
|
pub(crate) use bytestring_usage::*;
|
||||||
pub(crate) use collections_named_tuple::*;
|
pub(crate) use collections_named_tuple::*;
|
||||||
pub(crate) use complex_assignment_in_stub::*;
|
pub(crate) use complex_assignment_in_stub::*;
|
||||||
pub(crate) use complex_if_statement_in_stub::*;
|
pub(crate) use complex_if_statement_in_stub::*;
|
||||||
|
@ -42,6 +43,7 @@ pub(crate) use unused_private_type_definition::*;
|
||||||
mod any_eq_ne_annotation;
|
mod any_eq_ne_annotation;
|
||||||
mod bad_generator_return_type;
|
mod bad_generator_return_type;
|
||||||
mod bad_version_info_comparison;
|
mod bad_version_info_comparison;
|
||||||
|
mod bytestring_usage;
|
||||||
mod collections_named_tuple;
|
mod collections_named_tuple;
|
||||||
mod complex_assignment_in_stub;
|
mod complex_assignment_in_stub;
|
||||||
mod complex_if_statement_in_stub;
|
mod complex_if_statement_in_stub;
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||||
|
---
|
||||||
|
PYI057.py:4:20: PYI057 Do not use `typing.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
2 | import collections.abc
|
||||||
|
3 | import foo
|
||||||
|
4 | from typing import ByteString
|
||||||
|
| ^^^^^^^^^^ PYI057
|
||||||
|
5 | from collections.abc import ByteString
|
||||||
|
6 | from foo import ByteString
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI057.py:5:29: PYI057 Do not use `collections.abc.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
3 | import foo
|
||||||
|
4 | from typing import ByteString
|
||||||
|
5 | from collections.abc import ByteString
|
||||||
|
| ^^^^^^^^^^ PYI057
|
||||||
|
6 | from foo import ByteString
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI057.py:8:4: PYI057 Do not use `typing.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
6 | from foo import ByteString
|
||||||
|
7 |
|
||||||
|
8 | a: typing.ByteString
|
||||||
|
| ^^^^^^^^^^^^^^^^^ PYI057
|
||||||
|
9 | b: collections.abc.ByteString
|
||||||
|
10 | c: foo.ByteString
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI057.py:9:4: PYI057 Do not use `collections.abc.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
8 | a: typing.ByteString
|
||||||
|
9 | b: collections.abc.ByteString
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI057
|
||||||
|
10 | c: foo.ByteString
|
||||||
|
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||||
|
---
|
||||||
|
PYI057.pyi:4:20: PYI057 Do not use `typing.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
2 | import collections.abc
|
||||||
|
3 | import foo
|
||||||
|
4 | from typing import ByteString
|
||||||
|
| ^^^^^^^^^^ PYI057
|
||||||
|
5 | from collections.abc import ByteString
|
||||||
|
6 | from foo import ByteString
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI057.pyi:5:29: PYI057 Do not use `collections.abc.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
3 | import foo
|
||||||
|
4 | from typing import ByteString
|
||||||
|
5 | from collections.abc import ByteString
|
||||||
|
| ^^^^^^^^^^ PYI057
|
||||||
|
6 | from foo import ByteString
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI057.pyi:8:4: PYI057 Do not use `typing.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
6 | from foo import ByteString
|
||||||
|
7 |
|
||||||
|
8 | a: typing.ByteString
|
||||||
|
| ^^^^^^^^^^^^^^^^^ PYI057
|
||||||
|
9 | b: collections.abc.ByteString
|
||||||
|
10 | c: foo.ByteString
|
||||||
|
|
|
||||||
|
|
||||||
|
PYI057.pyi:9:4: PYI057 Do not use `collections.abc.ByteString`, which has unclear semantics and is deprecated
|
||||||
|
|
|
||||||
|
8 | a: typing.ByteString
|
||||||
|
9 | b: collections.abc.ByteString
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI057
|
||||||
|
10 | c: foo.ByteString
|
||||||
|
|
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -3576,6 +3576,7 @@
|
||||||
"PYI054",
|
"PYI054",
|
||||||
"PYI055",
|
"PYI055",
|
||||||
"PYI056",
|
"PYI056",
|
||||||
|
"PYI057",
|
||||||
"PYI058",
|
"PYI058",
|
||||||
"PYI059",
|
"PYI059",
|
||||||
"PYI06",
|
"PYI06",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue