Broaden appropriate flake8-pyi rules to check non-stub code too (#6297)

Of the rules that flake8-pyi enforces for `.pyi` type stubs, many of
them equally make sense to check in normal runtime code with type
annotations. Broaden these rules to check all files:

PYI013 ellipsis-in-non-empty-class-body
PYI016 duplicate-union-member
PYI018 unused-private-type-var
PYI019 custom-type-var-return-type
PYI024 collections-named-tuple
PYI025 unaliased-collections-abc-set-import
PYI030 unnecessary-literal-union
PYI032 any-eq-ne-annotation
PYI034 non-self-return-type
PYI036 bad-exit-annotation
PYI041 redundant-numeric-union
PYI042 snake-case-type-alias
PYI043 t-suffixed-type-alias
PYI045 iter-method-return-iterable
PYI046 unused-private-protocol
PYI047 unused-private-type-alias
PYI049 unused-private-typed-dict
PYI050 no-return-argument-annotation-in-stub (Python ≥ 3.11)
PYI051 redundant-literal-union
PYI056 unsupported-method-call-on-all

The other rules are stub-specific and remain enabled only in `.pyi`
files.

PYI001 unprefixed-type-param
PYI002 complex-if-statement-in-stub
PYI003 unrecognized-version-info-check
PYI004 patch-version-comparison
PYI005 wrong-tuple-length-version-comparison (could make sense to
broaden, see
https://github.com/astral-sh/ruff/pull/6297#issuecomment-1663314807)
PYI006 bad-version-info-comparison (same)
PYI007 unrecognized-platform-check
PYI008 unrecognized-platform-name
PYI009 pass-statement-stub-body
PYI010 non-empty-stub-body
PYI011 typed-argument-default-in-stub
PYI012 pass-in-class-body
PYI014 argument-default-in-stub
PYI015 assignment-default-in-stub
PYI017 complex-assignment-in-stub
PYI020 quoted-annotation-in-stub
PYI021 docstring-in-stub
PYI026 type-alias-without-annotation (could make sense to broaden, but
gives many false positives on runtime code as currently implemented)
PYI029 str-or-repr-defined-in-stub
PYI033 type-comment-in-stub
PYI035 unassigned-special-variable-in-stub
PYI044 future-annotations-in-stub
PYI048 stub-body-multiple-statements
PYI052 unannotated-assignment-in-stub
PYI053 string-or-bytes-too-long
PYI054 numeric-literal-too-long

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
This commit is contained in:
Anders Kaseorg
2023-08-03 08:40:42 -07:00
committed by GitHub
parent 30c2e9430e
commit 7c8bcede5b
30 changed files with 1274 additions and 137 deletions

View File

@@ -4,14 +4,14 @@ _S = TypeVar("_S", bound=BadClass)
_S2 = TypeVar("_S2", BadClass, GoodClass)
class BadClass:
def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # Ok
def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019
def bad_instance_method(self: _S, arg: bytes) -> _S: ... # Ok
def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
@classmethod
def bad_class_method(cls: type[_S], arg: int) -> _S: ... # Ok
def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019
@classmethod
@@ -32,10 +32,10 @@ class GoodClass:
# Python > 3.12
class PEP695BadDunderNew[T]:
def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # Ok
def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019
def generic_instance_method[S](self: S) -> S: ... # Ok
def generic_instance_method[S](self: S) -> S: ... # PYI019
class PEP695GoodDunderNew[T]:

View File

@@ -1,9 +1,11 @@
import collections
person: collections.namedtuple # OK
person: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
from collections import namedtuple
person: namedtuple # OK
person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
person = namedtuple("Person", ["name", "age"]) # OK
person = namedtuple(
"Person", ["name", "age"]
) # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"

View File

@@ -1,24 +1,38 @@
import typing
import typing_extensions
from typing import Literal
# Shouldn't emit for any cases in the non-stub file for compatibility with flake8-pyi.
# Note that this rule could be applied here in the future.
# Shouldn't affect non-union field types.
field1: Literal[1] # OK
field2: Literal[1] | Literal[2] # OK
def func1(arg1: Literal[1] | Literal[2]): # OK
# Should emit for duplicate field types.
field2: Literal[1] | Literal[2] # Error
# Should emit for union types in arguments.
def func1(arg1: Literal[1] | Literal[2]): # Error
print(arg1)
def func2() -> Literal[1] | Literal[2]: # OK
# Should emit for unions in return types.
def func2() -> Literal[1] | Literal[2]: # Error
return "my Literal[1]ing"
field3: Literal[1] | Literal[2] | str # OK
field4: str | Literal[1] | Literal[2] # OK
field5: Literal[1] | str | Literal[2] # OK
field6: Literal[1] | bool | Literal[2] | str # OK
field7 = Literal[1] | Literal[2] # OK
field8: Literal[1] | (Literal[2] | str) # OK
field9: Literal[1] | (Literal[2] | str) # OK
field10: (Literal[1] | str) | Literal[2] # OK
field11: dict[Literal[1] | Literal[2], str] # OK
# Should emit in longer unions, even if not directly adjacent.
field3: Literal[1] | Literal[2] | str # Error
field4: str | Literal[1] | Literal[2] # Error
field5: Literal[1] | str | Literal[2] # Error
field6: Literal[1] | bool | Literal[2] | str # Error
# Should emit for non-type unions.
field7 = Literal[1] | Literal[2] # Error
# Should emit for parenthesized unions.
field8: Literal[1] | (Literal[2] | str) # Error
# Should handle user parentheses when fixing.
field9: Literal[1] | (Literal[2] | str) # Error
field10: (Literal[1] | str) | Literal[2] # Error
# Should emit for union in generic parent type.
field11: dict[Literal[1] | Literal[2], str] # Error

View File

@@ -3,8 +3,8 @@ import typing
class Bad:
def __eq__(self, other: Any) -> bool: ... # Fine because not a stub file
def __ne__(self, other: typing.Any) -> typing.Any: ... # Fine because not a stub file
def __eq__(self, other: Any) -> bool: ... # Y032
def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
class Good:

View File

@@ -9,16 +9,16 @@ from typing import (
just_literals_pipe_union: TypeAlias = (
Literal[True] | Literal["idk"]
) # not PYI042 (not a stubfile)
) # PYI042, since not camel case
PublicAliasT: TypeAlias = str | int
PublicAliasT2: TypeAlias = Union[str, bytes]
_ABCDEFGHIJKLMNOPQRST: TypeAlias = typing.Any
_PrivateAliasS: TypeAlias = Literal["I", "guess", "this", "is", "okay"]
_PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
snake_case_alias1: TypeAlias = str | int # not PYI042 (not a stubfile)
_snake_case_alias2: TypeAlias = Literal["whatever"] # not PYI042 (not a stubfile)
Snake_case_alias: TypeAlias = int | float # not PYI042 (not a stubfile)
snake_case_alias1: TypeAlias = str | int # PYI042, since not camel case
_snake_case_alias2: TypeAlias = Literal["whatever"] # PYI042, since not camel case
Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
# check that this edge case doesn't crash
_: TypeAlias = str | int

View File

@@ -7,11 +7,11 @@ from typing import (
Literal,
)
_PrivateAliasT: TypeAlias = str | int # not PYI043 (not a stubfile)
_PrivateAliasT2: TypeAlias = typing.Any # not PYI043 (not a stubfile)
_PrivateAliasT: TypeAlias = str | int # PYI043, since this ends in a T
_PrivateAliasT2: TypeAlias = typing.Any # PYI043, since this ends in a T
_PrivateAliasT3: TypeAlias = Literal[
"not", "a", "chance"
] # not PYI043 (not a stubfile)
] # PYI043, since this ends in a T
just_literals_pipe_union: TypeAlias = Literal[True] | Literal["idk"]
PublicAliasT: TypeAlias = str | int
PublicAliasT2: TypeAlias = Union[str, bytes]

View File

@@ -56,13 +56,11 @@ pub(crate) fn bindings(checker: &mut Checker) {
checker.diagnostics.push(diagnostic);
}
}
if checker.is_stub {
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
if let Some(diagnostic) =
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding)
{
checker.diagnostics.push(diagnostic);
}
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
if let Some(diagnostic) =
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding)
{
checker.diagnostics.push(diagnostic);
}
}
}

View File

@@ -218,19 +218,17 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
}
}
if checker.is_stub {
if checker.enabled(Rule::UnusedPrivateTypeVar) {
flake8_pyi::rules::unused_private_type_var(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateProtocol) {
flake8_pyi::rules::unused_private_protocol(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypeAlias) {
flake8_pyi::rules::unused_private_type_alias(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypedDict) {
flake8_pyi::rules::unused_private_typed_dict(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypeVar) {
flake8_pyi::rules::unused_private_type_var(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateProtocol) {
flake8_pyi::rules::unused_private_protocol(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypeAlias) {
flake8_pyi::rules::unused_private_type_alias(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::UnusedPrivateTypedDict) {
flake8_pyi::rules::unused_private_typed_dict(checker, scope, &mut diagnostics);
}
if matches!(

View File

@@ -30,8 +30,8 @@ pub(crate) fn definitions(checker: &mut Checker) {
Rule::MissingTypeKwargs,
Rule::MissingTypeSelf,
]);
let enforce_stubs = checker.is_stub
&& checker.any_enabled(&[Rule::DocstringInStub, Rule::IterMethodReturnIterable]);
let enforce_stubs = checker.is_stub && checker.enabled(Rule::DocstringInStub);
let enforce_stubs_and_runtime = checker.enabled(Rule::IterMethodReturnIterable);
let enforce_docstrings = checker.any_enabled(&[
Rule::BlankLineAfterLastSection,
Rule::BlankLineAfterSummary,
@@ -81,7 +81,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
Rule::UndocumentedPublicPackage,
]);
if !enforce_annotations && !enforce_docstrings && !enforce_stubs {
if !enforce_annotations && !enforce_docstrings && !enforce_stubs && !enforce_stubs_and_runtime {
return;
}
@@ -141,6 +141,8 @@ pub(crate) fn definitions(checker: &mut Checker) {
if checker.enabled(Rule::DocstringInStub) {
flake8_pyi::rules::docstring_in_stubs(checker, docstring);
}
}
if enforce_stubs_and_runtime {
if checker.enabled(Rule::IterMethodReturnIterable) {
flake8_pyi::rules::iter_method_return_iterable(checker, definition);
}

View File

@@ -159,10 +159,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::NumpyDeprecatedFunction) {
numpy::rules::deprecated_function(checker, expr);
}
if checker.is_stub {
if checker.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(checker, expr);
}
if checker.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(checker, expr);
}
// Ex) List[...]
@@ -323,10 +321,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::PrivateMemberAccess) {
flake8_self::rules::private_member_access(checker, expr);
}
if checker.is_stub {
if checker.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(checker, expr);
}
if checker.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(checker, expr);
}
pandas_vet::rules::attr(checker, attr, value, expr);
}
@@ -868,7 +864,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::DjangoLocalsInRenderFunction) {
flake8_django::rules::locals_in_render_function(checker, call);
}
if checker.is_stub && checker.enabled(Rule::UnsupportedMethodCallOnAll) {
if checker.enabled(Rule::UnsupportedMethodCallOnAll) {
flake8_pyi::rules::unsupported_method_call_on_all(checker, func);
}
}
@@ -1079,35 +1075,33 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
);
}
}
if checker.is_stub {
if checker.enabled(Rule::DuplicateUnionMember)
&& checker.semantic.in_type_definition()
// Avoid duplicate checks if the parent is an `|`
&& !matches!(
checker.semantic.expr_parent(),
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
)
{
flake8_pyi::rules::duplicate_union_member(checker, expr);
}
if checker.enabled(Rule::UnnecessaryLiteralUnion)
// Avoid duplicate checks if the parent is an `|`
&& !matches!(
checker.semantic.expr_parent(),
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
)
{
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
}
if checker.enabled(Rule::RedundantLiteralUnion)
// Avoid duplicate checks if the parent is an `|`
&& !matches!(
checker.semantic.expr_parent(),
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
)
{
flake8_pyi::rules::redundant_literal_union(checker, expr);
}
if checker.enabled(Rule::DuplicateUnionMember)
&& checker.semantic.in_type_definition()
// Avoid duplicate checks if the parent is an `|`
&& !matches!(
checker.semantic.expr_parent(),
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
)
{
flake8_pyi::rules::duplicate_union_member(checker, expr);
}
if checker.enabled(Rule::UnnecessaryLiteralUnion)
// Avoid duplicate checks if the parent is an `|`
&& !matches!(
checker.semantic.expr_parent(),
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
)
{
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
}
if checker.enabled(Rule::RedundantLiteralUnion)
// Avoid duplicate checks if the parent is an `|`
&& !matches!(
checker.semantic.expr_parent(),
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
)
{
flake8_pyi::rules::redundant_literal_union(checker, expr);
}
}
Expr::UnaryOp(ast::ExprUnaryOp {

View File

@@ -143,47 +143,51 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::StubBodyMultipleStatements) {
flake8_pyi::rules::stub_body_multiple_statements(checker, stmt, body);
}
if checker.enabled(Rule::AnyEqNeAnnotation) {
flake8_pyi::rules::any_eq_ne_annotation(checker, name, parameters);
}
if checker.enabled(Rule::NonSelfReturnType) {
flake8_pyi::rules::non_self_return_type(
checker,
stmt,
name,
decorator_list,
returns.as_ref().map(AsRef::as_ref),
parameters,
stmt.is_async_function_def_stmt(),
);
}
if checker.enabled(Rule::CustomTypeVarReturnType) {
flake8_pyi::rules::custom_type_var_return_type(
checker,
name,
decorator_list,
returns.as_ref().map(AsRef::as_ref),
parameters,
type_params.as_ref(),
);
}
}
if checker.enabled(Rule::AnyEqNeAnnotation) {
flake8_pyi::rules::any_eq_ne_annotation(checker, name, parameters);
}
if checker.enabled(Rule::NonSelfReturnType) {
flake8_pyi::rules::non_self_return_type(
checker,
stmt,
name,
decorator_list,
returns.as_ref().map(AsRef::as_ref),
parameters,
stmt.is_async_function_def_stmt(),
);
}
if checker.enabled(Rule::CustomTypeVarReturnType) {
flake8_pyi::rules::custom_type_var_return_type(
checker,
name,
decorator_list,
returns.as_ref().map(AsRef::as_ref),
parameters,
type_params.as_ref(),
);
}
if checker.is_stub {
if checker.enabled(Rule::StrOrReprDefinedInStub) {
flake8_pyi::rules::str_or_repr_defined_in_stub(checker, stmt);
}
}
if checker.is_stub || checker.settings.target_version >= PythonVersion::Py311 {
if checker.enabled(Rule::NoReturnArgumentAnnotationInStub) {
flake8_pyi::rules::no_return_argument_annotation(checker, parameters);
}
if checker.enabled(Rule::BadExitAnnotation) {
flake8_pyi::rules::bad_exit_annotation(
checker,
stmt.is_async_function_def_stmt(),
name,
parameters,
);
}
if checker.enabled(Rule::RedundantNumericUnion) {
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
}
}
if checker.enabled(Rule::BadExitAnnotation) {
flake8_pyi::rules::bad_exit_annotation(
checker,
stmt.is_async_function_def_stmt(),
name,
parameters,
);
}
if checker.enabled(Rule::RedundantNumericUnion) {
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
}
if checker.enabled(Rule::DunderFunctionName) {
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(
@@ -470,9 +474,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::PassInClassBody) {
flake8_pyi::rules::pass_in_class_body(checker, stmt, body);
}
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, stmt, body);
}
}
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, stmt, body);
}
if checker.enabled(Rule::PytestIncorrectMarkParenthesesStyle) {
flake8_pytest_style::rules::marks(checker, decorator_list);
@@ -1407,13 +1411,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
);
}
}
if checker.semantic.match_typing_expr(annotation, "TypeAlias") {
if checker.enabled(Rule::SnakeCaseTypeAlias) {
flake8_pyi::rules::snake_case_type_alias(checker, target);
}
if checker.enabled(Rule::TSuffixedTypeAlias) {
flake8_pyi::rules::t_suffixed_type_alias(checker, target);
}
}
if checker.semantic.match_typing_expr(annotation, "TypeAlias") {
if checker.enabled(Rule::SnakeCaseTypeAlias) {
flake8_pyi::rules::snake_case_type_alias(checker, target);
}
if checker.enabled(Rule::TSuffixedTypeAlias) {
flake8_pyi::rules::t_suffixed_type_alias(checker, target);
}
}
}

View File

@@ -1,4 +1,149 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI013.py:3:5: PYI013 [*] Non-empty class body must not contain `...`
|
1 | class OneAttributeClass:
2 | value: int
3 | ...
| ^^^ PYI013
|
= help: Remove unnecessary `...`
Fix
1 1 | class OneAttributeClass:
2 2 | value: int
3 |- ...
4 3 |
5 4 |
6 5 | class OneAttributeClass2:
PYI013.py:7:5: PYI013 [*] Non-empty class body must not contain `...`
|
6 | class OneAttributeClass2:
7 | ...
| ^^^ PYI013
8 | value: int
|
= help: Remove unnecessary `...`
Fix
4 4 |
5 5 |
6 6 | class OneAttributeClass2:
7 |- ...
8 7 | value: int
9 8 |
10 9 |
PYI013.py:12:5: PYI013 [*] Non-empty class body must not contain `...`
|
11 | class TwoEllipsesClass:
12 | ...
| ^^^ PYI013
13 | ...
|
= help: Remove unnecessary `...`
Fix
10 10 |
11 11 | class TwoEllipsesClass:
12 12 | ...
13 |- ...
14 13 |
15 14 |
16 15 | class DocstringClass:
PYI013.py:13:5: PYI013 [*] Non-empty class body must not contain `...`
|
11 | class TwoEllipsesClass:
12 | ...
13 | ...
| ^^^ PYI013
|
= help: Remove unnecessary `...`
Fix
10 10 |
11 11 | class TwoEllipsesClass:
12 12 | ...
13 |- ...
14 13 |
15 14 |
16 15 | class DocstringClass:
PYI013.py:21:5: PYI013 [*] Non-empty class body must not contain `...`
|
19 | """
20 |
21 | ...
| ^^^ PYI013
|
= help: Remove unnecessary `...`
Fix
18 18 | My body only contains an ellipsis.
19 19 | """
20 20 |
21 |- ...
22 21 |
23 22 |
24 23 | class NonEmptyChild(Exception):
PYI013.py:26:5: PYI013 [*] Non-empty class body must not contain `...`
|
24 | class NonEmptyChild(Exception):
25 | value: int
26 | ...
| ^^^ PYI013
|
= help: Remove unnecessary `...`
Fix
23 23 |
24 24 | class NonEmptyChild(Exception):
25 25 | value: int
26 |- ...
27 26 |
28 27 |
29 28 | class NonEmptyChild2(Exception):
PYI013.py:30:5: PYI013 [*] Non-empty class body must not contain `...`
|
29 | class NonEmptyChild2(Exception):
30 | ...
| ^^^ PYI013
31 | value: int
|
= help: Remove unnecessary `...`
Fix
27 27 |
28 28 |
29 29 | class NonEmptyChild2(Exception):
30 |- ...
31 30 | value: int
32 31 |
33 32 |
PYI013.py:36:5: PYI013 [*] Non-empty class body must not contain `...`
|
34 | class NonEmptyWithInit:
35 | value: int
36 | ...
| ^^^ PYI013
37 |
38 | def __init__():
|
= help: Remove unnecessary `...`
Fix
33 33 |
34 34 | class NonEmptyWithInit:
35 35 | value: int
36 |- ...
37 36 |
38 37 | def __init__():
39 38 | pass

View File

@@ -1,4 +1,217 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI016.py:5:15: PYI016 [*] Duplicate union member `str`
|
4 | # Should emit for duplicate field types.
5 | field2: str | str # PYI016: Duplicate union member `str`
| ^^^ PYI016
|
= help: Remove duplicate union member `str`
Fix
2 2 | field1: str
3 3 |
4 4 | # Should emit for duplicate field types.
5 |-field2: str | str # PYI016: Duplicate union member `str`
5 |+field2: str # PYI016: Duplicate union member `str`
6 6 |
7 7 |
8 8 | # Should emit for union types in arguments.
PYI016.py:9:23: PYI016 [*] Duplicate union member `int`
|
8 | # Should emit for union types in arguments.
9 | def func1(arg1: int | int): # PYI016: Duplicate union member `int`
| ^^^ PYI016
10 | print(arg1)
|
= help: Remove duplicate union member `int`
Fix
6 6 |
7 7 |
8 8 | # Should emit for union types in arguments.
9 |-def func1(arg1: int | int): # PYI016: Duplicate union member `int`
9 |+def func1(arg1: int): # PYI016: Duplicate union member `int`
10 10 | print(arg1)
11 11 |
12 12 |
PYI016.py:14:22: PYI016 [*] Duplicate union member `str`
|
13 | # Should emit for unions in return types.
14 | def func2() -> str | str: # PYI016: Duplicate union member `str`
| ^^^ PYI016
15 | return "my string"
|
= help: Remove duplicate union member `str`
Fix
11 11 |
12 12 |
13 13 | # Should emit for unions in return types.
14 |-def func2() -> str | str: # PYI016: Duplicate union member `str`
14 |+def func2() -> str: # PYI016: Duplicate union member `str`
15 15 | return "my string"
16 16 |
17 17 |
PYI016.py:19:15: PYI016 [*] Duplicate union member `str`
|
18 | # Should emit in longer unions, even if not directly adjacent.
19 | field3: str | str | int # PYI016: Duplicate union member `str`
| ^^^ PYI016
20 | field4: int | int | str # PYI016: Duplicate union member `int`
21 | field5: str | int | str # PYI016: Duplicate union member `str`
|
= help: Remove duplicate union member `str`
Fix
16 16 |
17 17 |
18 18 | # Should emit in longer unions, even if not directly adjacent.
19 |-field3: str | str | int # PYI016: Duplicate union member `str`
19 |+field3: str | int # PYI016: Duplicate union member `str`
20 20 | field4: int | int | str # PYI016: Duplicate union member `int`
21 21 | field5: str | int | str # PYI016: Duplicate union member `str`
22 22 | field6: int | bool | str | int # PYI016: Duplicate union member `int`
PYI016.py:20:15: PYI016 [*] Duplicate union member `int`
|
18 | # Should emit in longer unions, even if not directly adjacent.
19 | field3: str | str | int # PYI016: Duplicate union member `str`
20 | field4: int | int | str # PYI016: Duplicate union member `int`
| ^^^ PYI016
21 | field5: str | int | str # PYI016: Duplicate union member `str`
22 | field6: int | bool | str | int # PYI016: Duplicate union member `int`
|
= help: Remove duplicate union member `int`
Fix
17 17 |
18 18 | # Should emit in longer unions, even if not directly adjacent.
19 19 | field3: str | str | int # PYI016: Duplicate union member `str`
20 |-field4: int | int | str # PYI016: Duplicate union member `int`
20 |+field4: int | str # PYI016: Duplicate union member `int`
21 21 | field5: str | int | str # PYI016: Duplicate union member `str`
22 22 | field6: int | bool | str | int # PYI016: Duplicate union member `int`
23 23 |
PYI016.py:21:21: PYI016 [*] Duplicate union member `str`
|
19 | field3: str | str | int # PYI016: Duplicate union member `str`
20 | field4: int | int | str # PYI016: Duplicate union member `int`
21 | field5: str | int | str # PYI016: Duplicate union member `str`
| ^^^ PYI016
22 | field6: int | bool | str | int # PYI016: Duplicate union member `int`
|
= help: Remove duplicate union member `str`
Fix
18 18 | # Should emit in longer unions, even if not directly adjacent.
19 19 | field3: str | str | int # PYI016: Duplicate union member `str`
20 20 | field4: int | int | str # PYI016: Duplicate union member `int`
21 |-field5: str | int | str # PYI016: Duplicate union member `str`
21 |+field5: str | int # PYI016: Duplicate union member `str`
22 22 | field6: int | bool | str | int # PYI016: Duplicate union member `int`
23 23 |
24 24 | # Shouldn't emit for non-type unions.
PYI016.py:22:28: PYI016 [*] Duplicate union member `int`
|
20 | field4: int | int | str # PYI016: Duplicate union member `int`
21 | field5: str | int | str # PYI016: Duplicate union member `str`
22 | field6: int | bool | str | int # PYI016: Duplicate union member `int`
| ^^^ PYI016
23 |
24 | # Shouldn't emit for non-type unions.
|
= help: Remove duplicate union member `int`
Fix
19 19 | field3: str | str | int # PYI016: Duplicate union member `str`
20 20 | field4: int | int | str # PYI016: Duplicate union member `int`
21 21 | field5: str | int | str # PYI016: Duplicate union member `str`
22 |-field6: int | bool | str | int # PYI016: Duplicate union member `int`
22 |+field6: int | bool | str # PYI016: Duplicate union member `int`
23 23 |
24 24 | # Shouldn't emit for non-type unions.
25 25 | field7 = str | str
PYI016.py:28:22: PYI016 [*] Duplicate union member `int`
|
27 | # Should emit for strangely-bracketed unions.
28 | field8: int | (str | int) # PYI016: Duplicate union member `int`
| ^^^ PYI016
29 |
30 | # Should handle user brackets when fixing.
|
= help: Remove duplicate union member `int`
Fix
25 25 | field7 = str | str
26 26 |
27 27 | # Should emit for strangely-bracketed unions.
28 |-field8: int | (str | int) # PYI016: Duplicate union member `int`
28 |+field8: int | (str) # PYI016: Duplicate union member `int`
29 29 |
30 30 | # Should handle user brackets when fixing.
31 31 | field9: int | (int | str) # PYI016: Duplicate union member `int`
PYI016.py:31:16: PYI016 [*] Duplicate union member `int`
|
30 | # Should handle user brackets when fixing.
31 | field9: int | (int | str) # PYI016: Duplicate union member `int`
| ^^^ PYI016
32 | field10: (str | int) | str # PYI016: Duplicate union member `str`
|
= help: Remove duplicate union member `int`
Fix
28 28 | field8: int | (str | int) # PYI016: Duplicate union member `int`
29 29 |
30 30 | # Should handle user brackets when fixing.
31 |-field9: int | (int | str) # PYI016: Duplicate union member `int`
31 |+field9: int | (str) # PYI016: Duplicate union member `int`
32 32 | field10: (str | int) | str # PYI016: Duplicate union member `str`
33 33 |
34 34 | # Should emit for nested unions.
PYI016.py:32:24: PYI016 [*] Duplicate union member `str`
|
30 | # Should handle user brackets when fixing.
31 | field9: int | (int | str) # PYI016: Duplicate union member `int`
32 | field10: (str | int) | str # PYI016: Duplicate union member `str`
| ^^^ PYI016
33 |
34 | # Should emit for nested unions.
|
= help: Remove duplicate union member `str`
Fix
29 29 |
30 30 | # Should handle user brackets when fixing.
31 31 | field9: int | (int | str) # PYI016: Duplicate union member `int`
32 |-field10: (str | int) | str # PYI016: Duplicate union member `str`
32 |+field10: str | int # PYI016: Duplicate union member `str`
33 33 |
34 34 | # Should emit for nested unions.
35 35 | field11: dict[int | int, str]
PYI016.py:35:21: PYI016 [*] Duplicate union member `int`
|
34 | # Should emit for nested unions.
35 | field11: dict[int | int, str]
| ^^^ PYI016
|
= help: Remove duplicate union member `int`
Fix
32 32 | field10: (str | int) | str # PYI016: Duplicate union member `str`
33 33 |
34 34 | # Should emit for nested unions.
35 |-field11: dict[int | int, str]
35 |+field11: dict[int, str]

View File

@@ -1,4 +1,22 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI018.py:4:1: PYI018 Private TypeVar `_T` is never used
|
2 | from typing import TypeVar
3 |
4 | _T = typing.TypeVar("_T")
| ^^ PYI018
5 | _P = TypeVar("_P")
|
PYI018.py:5:1: PYI018 Private TypeVar `_P` is never used
|
4 | _T = typing.TypeVar("_T")
5 | _P = TypeVar("_P")
| ^^ PYI018
6 |
7 | # OK
|

View File

@@ -1,4 +1,38 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI019.py:7:62: PYI019 Methods like `__new__` should return `typing.Self` instead of a custom `TypeVar`
|
6 | class BadClass:
7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019
| ^^ PYI019
|
PYI019.py:10:54: PYI019 Methods like `bad_instance_method` should return `typing.Self` instead of a custom `TypeVar`
|
10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
| ^^ PYI019
|
PYI019.py:14:54: PYI019 Methods like `bad_class_method` should return `typing.Self` instead of a custom `TypeVar`
|
13 | @classmethod
14 | def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019
| ^^ PYI019
|
PYI019.py:35:63: PYI019 Methods like `__new__` should return `typing.Self` instead of a custom `TypeVar`
|
33 | # Python > 3.12
34 | class PEP695BadDunderNew[T]:
35 | def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019
| ^ PYI019
|
PYI019.py:38:46: PYI019 Methods like `generic_instance_method` should return `typing.Self` instead of a custom `TypeVar`
|
38 | def generic_instance_method[S](self: S) -> S: ... # PYI019
| ^ PYI019
|

View File

@@ -1,4 +1,37 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI024.py:3:9: PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple`
|
1 | import collections
2 |
3 | person: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
| ^^^^^^^^^^^^^^^^^^^^^^ PYI024
4 |
5 | from collections import namedtuple
|
= help: Replace with `typing.NamedTuple`
PYI024.py:7:9: PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple`
|
5 | from collections import namedtuple
6 |
7 | person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
| ^^^^^^^^^^ PYI024
8 |
9 | person = namedtuple(
|
= help: Replace with `typing.NamedTuple`
PYI024.py:9:10: PYI024 Use `typing.NamedTuple` instead of `collections.namedtuple`
|
7 | person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
8 |
9 | person = namedtuple(
| ^^^^^^^^^^ PYI024
10 | "Person", ["name", "age"]
11 | ) # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
= help: Replace with `typing.NamedTuple`

View File

@@ -1,4 +1,46 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI025.py:10:33: PYI025 [*] Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin
|
9 | def f():
10 | from collections.abc import Set # PYI025
| ^^^ PYI025
|
= help: Alias `Set` to `AbstractSet`
Suggested fix
7 7 |
8 8 |
9 9 | def f():
10 |- from collections.abc import Set # PYI025
10 |+ from collections.abc import Set as AbstractSet # PYI025
11 11 |
12 12 |
13 13 | def f():
PYI025.py:14:51: PYI025 [*] Use `from collections.abc import Set as AbstractSet` to avoid confusion with the `set` builtin
|
13 | def f():
14 | from collections.abc import Container, Sized, Set, ValuesView # PYI025
| ^^^ PYI025
15 |
16 | GLOBAL: Set[int] = set()
|
= help: Alias `Set` to `AbstractSet`
Suggested fix
11 11 |
12 12 |
13 13 | def f():
14 |- from collections.abc import Container, Sized, Set, ValuesView # PYI025
14 |+ from collections.abc import Container, Sized, Set as AbstractSet, ValuesView # PYI025
15 15 |
16 |- GLOBAL: Set[int] = set()
16 |+ GLOBAL: AbstractSet[int] = set()
17 17 |
18 18 | class Class:
19 |- member: Set[int]
19 |+ member: AbstractSet[int]

View File

@@ -1,4 +1,110 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI030.py:9:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
8 | # Should emit for duplicate field types.
9 | field2: Literal[1] | Literal[2] # Error
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI030
10 |
11 | # Should emit for union types in arguments.
|
PYI030.py:12:17: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
11 | # Should emit for union types in arguments.
12 | def func1(arg1: Literal[1] | Literal[2]): # Error
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI030
13 | print(arg1)
|
PYI030.py:17:16: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
16 | # Should emit for unions in return types.
17 | def func2() -> Literal[1] | Literal[2]: # Error
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI030
18 | return "my Literal[1]ing"
|
PYI030.py:22:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
21 | # Should emit in longer unions, even if not directly adjacent.
22 | field3: Literal[1] | Literal[2] | str # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
23 | field4: str | Literal[1] | Literal[2] # Error
24 | field5: Literal[1] | str | Literal[2] # Error
|
PYI030.py:23:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
21 | # Should emit in longer unions, even if not directly adjacent.
22 | field3: Literal[1] | Literal[2] | str # Error
23 | field4: str | Literal[1] | Literal[2] # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
24 | field5: Literal[1] | str | Literal[2] # Error
25 | field6: Literal[1] | bool | Literal[2] | str # Error
|
PYI030.py:24:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
22 | field3: Literal[1] | Literal[2] | str # Error
23 | field4: str | Literal[1] | Literal[2] # Error
24 | field5: Literal[1] | str | Literal[2] # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
25 | field6: Literal[1] | bool | Literal[2] | str # Error
|
PYI030.py:25:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
23 | field4: str | Literal[1] | Literal[2] # Error
24 | field5: Literal[1] | str | Literal[2] # Error
25 | field6: Literal[1] | bool | Literal[2] | str # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
26 |
27 | # Should emit for non-type unions.
|
PYI030.py:28:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
27 | # Should emit for non-type unions.
28 | field7 = Literal[1] | Literal[2] # Error
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI030
29 |
30 | # Should emit for parenthesized unions.
|
PYI030.py:31:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
30 | # Should emit for parenthesized unions.
31 | field8: Literal[1] | (Literal[2] | str) # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
32 |
33 | # Should handle user parentheses when fixing.
|
PYI030.py:34:9: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
33 | # Should handle user parentheses when fixing.
34 | field9: Literal[1] | (Literal[2] | str) # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
35 | field10: (Literal[1] | str) | Literal[2] # Error
|
PYI030.py:35:10: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
33 | # Should handle user parentheses when fixing.
34 | field9: Literal[1] | (Literal[2] | str) # Error
35 | field10: (Literal[1] | str) | Literal[2] # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI030
36 |
37 | # Should emit for union in generic parent type.
|
PYI030.py:38:15: PYI030 Multiple literal members in a union. Use a single literal, e.g. `Literal[1, 2]`
|
37 | # Should emit for union in generic parent type.
38 | field11: dict[Literal[1] | Literal[2], str] # Error
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI030
|

View File

@@ -1,4 +1,42 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI032.py:6:29: PYI032 [*] Prefer `object` to `Any` for the second parameter to `__eq__`
|
5 | class Bad:
6 | def __eq__(self, other: Any) -> bool: ... # Y032
| ^^^ PYI032
7 | def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
|
= help: Replace with `object`
Fix
3 3 |
4 4 |
5 5 | class Bad:
6 |- def __eq__(self, other: Any) -> bool: ... # Y032
6 |+ def __eq__(self, other: object) -> bool: ... # Y032
7 7 | def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
8 8 |
9 9 |
PYI032.py:7:29: PYI032 [*] Prefer `object` to `Any` for the second parameter to `__ne__`
|
5 | class Bad:
6 | def __eq__(self, other: Any) -> bool: ... # Y032
7 | def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
| ^^^^^^^^^^ PYI032
|
= help: Replace with `object`
Fix
4 4 |
5 5 | class Bad:
6 6 | def __eq__(self, other: Any) -> bool: ... # Y032
7 |- def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
7 |+ def __ne__(self, other: object) -> typing.Any: ... # Y032
8 8 |
9 9 |
10 10 | class Good:

View File

@@ -1,4 +1,92 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI034.py:19:9: PYI034 `__new__` methods in classes like `Bad` usually return `self` at runtime
|
17 | object
18 | ): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
19 | def __new__(cls, *args: Any, **kwargs: Any) -> Bad:
| ^^^^^^^ PYI034
20 | ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:34:9: PYI034 `__enter__` methods in classes like `Bad` usually return `self` at runtime
|
32 | ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
33 |
34 | def __enter__(self) -> Bad:
| ^^^^^^^^^ PYI034
35 | ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:37:15: PYI034 `__aenter__` methods in classes like `Bad` usually return `self` at runtime
|
35 | ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..."
36 |
37 | async def __aenter__(self) -> Bad:
| ^^^^^^^^^^ PYI034
38 | ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:40:9: PYI034 `__iadd__` methods in classes like `Bad` usually return `self` at runtime
|
38 | ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..."
39 |
40 | def __iadd__(self, other: Bad) -> Bad:
| ^^^^^^^^ PYI034
41 | ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:163:9: PYI034 `__iter__` methods in classes like `BadIterator1` usually return `self` at runtime
|
162 | class BadIterator1(Iterator[int]):
163 | def __iter__(self) -> Iterator[int]:
| ^^^^^^^^ PYI034
164 | ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:170:9: PYI034 `__iter__` methods in classes like `BadIterator2` usually return `self` at runtime
|
168 | typing.Iterator[int]
169 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
170 | def __iter__(self) -> Iterator[int]:
| ^^^^^^^^ PYI034
171 | ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:177:9: PYI034 `__iter__` methods in classes like `BadIterator3` usually return `self` at runtime
|
175 | typing.Iterator[int]
176 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
177 | def __iter__(self) -> collections.abc.Iterator[int]:
| ^^^^^^^^ PYI034
178 | ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:183:9: PYI034 `__iter__` methods in classes like `BadIterator4` usually return `self` at runtime
|
181 | class BadIterator4(Iterator[int]):
182 | # Note: *Iterable*, not *Iterator*, returned!
183 | def __iter__(self) -> Iterable[int]:
| ^^^^^^^^ PYI034
184 | ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..."
|
= help: Consider using `typing_extensions.Self` as return type
PYI034.py:193:9: PYI034 `__aiter__` methods in classes like `BadAsyncIterator` usually return `self` at runtime
|
192 | class BadAsyncIterator(collections.abc.AsyncIterator[str]):
193 | def __aiter__(self) -> typing.AsyncIterator[str]:
| ^^^^^^^^^ PYI034
194 | ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax)
|
= help: Consider using `typing_extensions.Self` as return type

View File

@@ -1,4 +1,161 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI036.py:54:31: PYI036 [*] Star-args in `__exit__` should be annotated with `object`
|
53 | class BadOne:
54 | def __exit__(self, *args: Any) -> None: ... # PYI036: Bad star-args annotation
| ^^^ PYI036
55 | async def __aexit__(self) -> None: ... # PYI036: Missing args
|
= help: Annotate star-args with `object`
Fix
51 51 |
52 52 |
53 53 | class BadOne:
54 |- def __exit__(self, *args: Any) -> None: ... # PYI036: Bad star-args annotation
54 |+ def __exit__(self, *args: object) -> None: ... # PYI036: Bad star-args annotation
55 55 | async def __aexit__(self) -> None: ... # PYI036: Missing args
56 56 |
57 57 | class BadTwo:
PYI036.py:55:24: PYI036 If there are no star-args, `__aexit__` should have at least 3 non-keyword-only args (excluding `self`)
|
53 | class BadOne:
54 | def __exit__(self, *args: Any) -> None: ... # PYI036: Bad star-args annotation
55 | async def __aexit__(self) -> None: ... # PYI036: Missing args
| ^^^^^^ PYI036
56 |
57 | class BadTwo:
|
PYI036.py:58:38: PYI036 All arguments after the first four in `__exit__` must have a default value
|
57 | class BadTwo:
58 | def __exit__(self, typ, exc, tb, weird_extra_arg) -> None: ... # PYI036: Extra arg must have default
| ^^^^^^^^^^^^^^^ PYI036
59 | async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ...# PYI036: Extra arg must have default
|
PYI036.py:59:48: PYI036 All keyword-only arguments in `__aexit__` must have a default value
|
57 | class BadTwo:
58 | def __exit__(self, typ, exc, tb, weird_extra_arg) -> None: ... # PYI036: Extra arg must have default
59 | async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ...# PYI036: Extra arg must have default
| ^^^^^^^^^^^^^^^ PYI036
60 |
61 | class BadThree:
|
PYI036.py:62:29: PYI036 The first argument in `__exit__` should be annotated with `object` or `type[BaseException] | None`
|
61 | class BadThree:
62 | def __exit__(self, typ: type[BaseException], exc: BaseException | None, tb: TracebackType | None) -> None: ... # PYI036: First arg has bad annotation
| ^^^^^^^^^^^^^^^^^^^ PYI036
63 | async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException, __tb: TracebackType) -> bool | None: ... # PYI036: Second arg has bad annotation
|
PYI036.py:63:73: PYI036 The second argument in `__aexit__` should be annotated with `object` or `BaseException | None`
|
61 | class BadThree:
62 | def __exit__(self, typ: type[BaseException], exc: BaseException | None, tb: TracebackType | None) -> None: ... # PYI036: First arg has bad annotation
63 | async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException, __tb: TracebackType) -> bool | None: ... # PYI036: Second arg has bad annotation
| ^^^^^^^^^^^^^ PYI036
64 |
65 | class BadFour:
|
PYI036.py:63:94: PYI036 The third argument in `__aexit__` should be annotated with `object` or `types.TracebackType | None`
|
61 | class BadThree:
62 | def __exit__(self, typ: type[BaseException], exc: BaseException | None, tb: TracebackType | None) -> None: ... # PYI036: First arg has bad annotation
63 | async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException, __tb: TracebackType) -> bool | None: ... # PYI036: Second arg has bad annotation
| ^^^^^^^^^^^^^ PYI036
64 |
65 | class BadFour:
|
PYI036.py:66:111: PYI036 The third argument in `__exit__` should be annotated with `object` or `types.TracebackType | None`
|
65 | class BadFour:
66 | def __exit__(self, typ: typing.Optional[type[BaseException]], exc: typing.Union[BaseException, None], tb: TracebackType) -> None: ... # PYI036: Third arg has bad annotation
| ^^^^^^^^^^^^^ PYI036
67 | async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: typing.Union[TracebackType, None, int]) -> bool | None: ... # PYI036: Third arg has bad annotation
|
PYI036.py:67:101: PYI036 The third argument in `__aexit__` should be annotated with `object` or `types.TracebackType | None`
|
65 | class BadFour:
66 | def __exit__(self, typ: typing.Optional[type[BaseException]], exc: typing.Union[BaseException, None], tb: TracebackType) -> None: ... # PYI036: Third arg has bad annotation
67 | async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: typing.Union[TracebackType, None, int]) -> bool | None: ... # PYI036: Third arg has bad annotation
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI036
68 |
69 | class BadFive:
|
PYI036.py:70:29: PYI036 The first argument in `__exit__` should be annotated with `object` or `type[BaseException] | None`
|
69 | class BadFive:
70 | def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
| ^^^^^^^^^^^^^^^^^^^^ PYI036
71 | async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
|
PYI036.py:70:58: PYI036 [*] Star-args in `__exit__` should be annotated with `object`
|
69 | class BadFive:
70 | def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
| ^^^^^^^^^ PYI036
71 | async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
|
= help: Annotate star-args with `object`
Fix
67 67 | async def __aexit__(self, __typ: type[BaseException] | None, __exc: BaseException | None, __tb: typing.Union[TracebackType, None, int]) -> bool | None: ... # PYI036: Third arg has bad annotation
68 68 |
69 69 | class BadFive:
70 |- def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
70 |+ def __exit__(self, typ: BaseException | None, *args: object) -> bool: ... # PYI036: Bad star-args annotation
71 71 | async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
72 72 |
73 73 | class BadSix:
PYI036.py:71:74: PYI036 [*] Star-args in `__aexit__` should be annotated with `object`
|
69 | class BadFive:
70 | def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
71 | async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
| ^^^ PYI036
72 |
73 | class BadSix:
|
= help: Annotate star-args with `object`
Fix
68 68 |
69 69 | class BadFive:
70 70 | def __exit__(self, typ: BaseException | None, *args: list[str]) -> bool: ... # PYI036: Bad star-args annotation
71 |- async def __aexit__(self, /, typ: type[BaseException] | None, *args: Any) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
71 |+ async def __aexit__(self, /, typ: type[BaseException] | None, *args: object) -> Awaitable[None]: ... # PYI036: Bad star-args annotation
72 72 |
73 73 | class BadSix:
74 74 | def __exit__(self, typ, exc, tb, weird_extra_arg, extra_arg2 = None) -> None: ... # PYI036: Extra arg must have default
PYI036.py:74:38: PYI036 All arguments after the first four in `__exit__` must have a default value
|
73 | class BadSix:
74 | def __exit__(self, typ, exc, tb, weird_extra_arg, extra_arg2 = None) -> None: ... # PYI036: Extra arg must have default
| ^^^^^^^^^^^^^^^ PYI036
75 | async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ... # PYI036: kwargs must have default
|
PYI036.py:75:48: PYI036 All keyword-only arguments in `__aexit__` must have a default value
|
73 | class BadSix:
74 | def __exit__(self, typ, exc, tb, weird_extra_arg, extra_arg2 = None) -> None: ... # PYI036: Extra arg must have default
75 | async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ... # PYI036: kwargs must have default
| ^^^^^^^^^^^^^^^ PYI036
|

View File

@@ -1,4 +1,50 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI041.py:22:14: PYI041 Use `float` instead of `int | float`
|
22 | def f0(arg1: float | int) -> None:
| ^^^^^^^^^^^ PYI041
23 | ...
|
PYI041.py:26:30: PYI041 Use `complex` instead of `float | complex`
|
26 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041
27 | ...
|
PYI041.py:30:28: PYI041 Use `float` instead of `int | float`
|
30 | def f2(arg1: int, /, arg2: int | int | float) -> None:
| ^^^^^^^^^^^^^^^^^ PYI041
31 | ...
|
PYI041.py:38:24: PYI041 Use `float` instead of `int | float`
|
38 | async def f4(**kwargs: int | int | float) -> None:
| ^^^^^^^^^^^^^^^^^ PYI041
39 | ...
|
PYI041.py:46:24: PYI041 Use `complex` instead of `float | complex`
|
44 | ...
45 |
46 | def bad(self, arg: int | float | complex) -> None:
| ^^^^^^^^^^^^^^^^^^^^^ PYI041
47 | ...
|
PYI041.py:46:24: PYI041 Use `complex` instead of `int | complex`
|
44 | ...
45 |
46 | def bad(self, arg: int | float | complex) -> None:
| ^^^^^^^^^^^^^^^^^^^^^ PYI041
47 | ...
|

View File

@@ -1,4 +1,32 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI042.py:10:1: PYI042 Type alias `just_literals_pipe_union` should be CamelCase
|
8 | )
9 |
10 | just_literals_pipe_union: TypeAlias = (
| ^^^^^^^^^^^^^^^^^^^^^^^^ PYI042
11 | Literal[True] | Literal["idk"]
12 | ) # PYI042, since not camel case
|
PYI042.py:19:1: PYI042 Type alias `snake_case_alias1` should be CamelCase
|
17 | _PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
18 |
19 | snake_case_alias1: TypeAlias = str | int # PYI042, since not camel case
| ^^^^^^^^^^^^^^^^^ PYI042
20 | _snake_case_alias2: TypeAlias = Literal["whatever"] # PYI042, since not camel case
21 | Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
PYI042.py:20:1: PYI042 Type alias `_snake_case_alias2` should be CamelCase
|
19 | snake_case_alias1: TypeAlias = str | int # PYI042, since not camel case
20 | _snake_case_alias2: TypeAlias = Literal["whatever"] # PYI042, since not camel case
| ^^^^^^^^^^^^^^^^^^ PYI042
21 | Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|

View File

@@ -1,4 +1,33 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI043.py:10:1: PYI043 Private type alias `_PrivateAliasT` should not be suffixed with `T` (the `T` suffix implies that an object is a `TypeVar`)
|
8 | )
9 |
10 | _PrivateAliasT: TypeAlias = str | int # PYI043, since this ends in a T
| ^^^^^^^^^^^^^^ PYI043
11 | _PrivateAliasT2: TypeAlias = typing.Any # PYI043, since this ends in a T
12 | _PrivateAliasT3: TypeAlias = Literal[
|
PYI043.py:11:1: PYI043 Private type alias `_PrivateAliasT2` should not be suffixed with `T` (the `T` suffix implies that an object is a `TypeVar`)
|
10 | _PrivateAliasT: TypeAlias = str | int # PYI043, since this ends in a T
11 | _PrivateAliasT2: TypeAlias = typing.Any # PYI043, since this ends in a T
| ^^^^^^^^^^^^^^^ PYI043
12 | _PrivateAliasT3: TypeAlias = Literal[
13 | "not", "a", "chance"
|
PYI043.py:12:1: PYI043 Private type alias `_PrivateAliasT3` should not be suffixed with `T` (the `T` suffix implies that an object is a `TypeVar`)
|
10 | _PrivateAliasT: TypeAlias = str | int # PYI043, since this ends in a T
11 | _PrivateAliasT2: TypeAlias = typing.Any # PYI043, since this ends in a T
12 | _PrivateAliasT3: TypeAlias = Literal[
| ^^^^^^^^^^^^^^^ PYI043
13 | "not", "a", "chance"
14 | ] # PYI043, since this ends in a T
|

View File

@@ -1,4 +1,60 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI045.py:12:27: PYI045 `__iter__` methods should return an `Iterator`, not an `Iterable`
|
11 | class TypingIterableTReturn:
12 | def __iter__(self) -> typing.Iterable[int]:
| ^^^^^^^^^^^^^^^^^^^^ PYI045
13 | ...
|
PYI045.py:20:27: PYI045 `__iter__` methods should return an `Iterator`, not an `Iterable`
|
19 | class TypingIterableReturn:
20 | def __iter__(self) -> typing.Iterable:
| ^^^^^^^^^^^^^^^ PYI045
21 | ...
|
PYI045.py:28:27: PYI045 `__iter__` methods should return an `Iterator`, not an `Iterable`
|
27 | class CollectionsIterableTReturn:
28 | def __iter__(self) -> collections.abc.Iterable[int]:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI045
29 | ...
|
PYI045.py:36:27: PYI045 `__iter__` methods should return an `Iterator`, not an `Iterable`
|
35 | class CollectionsIterableReturn:
36 | def __iter__(self) -> collections.abc.Iterable:
| ^^^^^^^^^^^^^^^^^^^^^^^^ PYI045
37 | ...
|
PYI045.py:44:27: PYI045 `__iter__` methods should return an `Iterator`, not an `Iterable`
|
43 | class IterableReturn:
44 | def __iter__(self) -> Iterable:
| ^^^^^^^^ PYI045
45 | ...
|
PYI045.py:79:28: PYI045 `__aiter__` methods should return an `AsyncIterator`, not an `AsyncIterable`
|
78 | class TypingAsyncIterableTReturn:
79 | def __aiter__(self) -> typing.AsyncIterable[int]:
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI045
80 | ...
|
PYI045.py:84:28: PYI045 `__aiter__` methods should return an `AsyncIterator`, not an `AsyncIterable`
|
83 | class TypingAsyncIterableReturn:
84 | def __aiter__(self) -> typing.AsyncIterable:
| ^^^^^^^^^^^^^^^^^^^^ PYI045
85 | ...
|

View File

@@ -1,4 +1,18 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI046.py:5:7: PYI046 Private protocol `_Foo` is never used
|
5 | class _Foo(Protocol):
| ^^^^ PYI046
6 | bar: int
|
PYI046.py:9:7: PYI046 Private protocol `_Bar` is never used
|
9 | class _Bar(typing.Protocol):
| ^^^^ PYI046
10 | bar: int
|

View File

@@ -1,4 +1,20 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI047.py:6:1: PYI047 Private TypeAlias `_UnusedPrivateTypeAlias` is never used
|
6 | _UnusedPrivateTypeAlias: TypeAlias = int | None
| ^^^^^^^^^^^^^^^^^^^^^^^ PYI047
7 | _T: typing.TypeAlias = str
|
PYI047.py:7:1: PYI047 Private TypeAlias `_T` is never used
|
6 | _UnusedPrivateTypeAlias: TypeAlias = int | None
7 | _T: typing.TypeAlias = str
| ^^ PYI047
8 |
9 | # OK
|

View File

@@ -1,4 +1,18 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI049.py:5:7: PYI049 Private TypedDict `_UnusedTypedDict` is never used
|
5 | class _UnusedTypedDict(TypedDict):
| ^^^^^^^^^^^^^^^^ PYI049
6 | foo: str
|
PYI049.py:9:7: PYI049 Private TypedDict `_UnusedTypedDict2` is never used
|
9 | class _UnusedTypedDict2(typing.TypedDict):
| ^^^^^^^^^^^^^^^^^ PYI049
10 | bar: int
|

View File

@@ -1,4 +1,24 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI051.py:4:18: PYI051 `Literal["foo"]` is redundant in a union with `str`
|
2 | from typing import Literal, TypeAlias, Union
3 |
4 | A: str | Literal["foo"]
| ^^^^^ PYI051
5 | B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
6 | C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
|
PYI051.py:9:31: PYI051 `Literal[1J]` is redundant in a union with `complex`
|
7 | D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
8 |
9 | def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
| ^^ PYI051
10 |
11 | # OK
|

View File

@@ -1,4 +1,32 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI056.py:4:1: PYI056 Calling `.append()` on `__all__` may not be supported by all type checkers (use `+=` instead)
|
3 | # Errors
4 | __all__.append("D")
| ^^^^^^^^^^^^^^ PYI056
5 | __all__.extend(["E", "Foo"])
6 | __all__.remove("A")
|
PYI056.py:5:1: PYI056 Calling `.extend()` on `__all__` may not be supported by all type checkers (use `+=` instead)
|
3 | # Errors
4 | __all__.append("D")
5 | __all__.extend(["E", "Foo"])
| ^^^^^^^^^^^^^^ PYI056
6 | __all__.remove("A")
|
PYI056.py:6:1: PYI056 Calling `.remove()` on `__all__` may not be supported by all type checkers (use `+=` instead)
|
4 | __all__.append("D")
5 | __all__.extend(["E", "Foo"])
6 | __all__.remove("A")
| ^^^^^^^^^^^^^^ PYI056
7 |
8 | # OK
|