mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[flake8-pyi
] Also remove self
and cls
's annotation (PYI034
) (#14801)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
parent
0e9427255f
commit
aa6b812a73
8 changed files with 785 additions and 73 deletions
|
@ -8,7 +8,7 @@ import typing
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||
from enum import EnumMeta
|
||||
from typing import Any, overload
|
||||
from typing import Any, Generic, ParamSpec, Type, TypeVar, TypeVarTuple, overload
|
||||
|
||||
import typing_extensions
|
||||
from _typeshed import Self
|
||||
|
@ -321,3 +321,41 @@ def __imul__(self, other: Any) -> list[str]:
|
|||
class UsesStringizedAnnotations:
|
||||
def __iadd__(self, other: "UsesStringizedAnnotations") -> "typing.Self":
|
||||
return self
|
||||
|
||||
|
||||
class NonGeneric1(tuple):
|
||||
def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
|
||||
class NonGeneric2(tuple):
|
||||
def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
|
||||
class Generic1[T](list):
|
||||
def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
def __enter__(self: Generic1) -> Generic1: ...
|
||||
|
||||
|
||||
### Correctness of typevar-likes are not verified.
|
||||
|
||||
T = TypeVar('T')
|
||||
P = ParamSpec()
|
||||
Ts = TypeVarTuple('foo')
|
||||
|
||||
class Generic2(Generic[T]):
|
||||
def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
def __enter__(self: Generic2) -> Generic2: ...
|
||||
|
||||
class Generic3(tuple[*Ts]):
|
||||
def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
def __enter__(self: Generic3) -> Generic3: ...
|
||||
|
||||
class Generic4(collections.abc.Callable[P, ...]):
|
||||
def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
def __enter__(self: Generic4) -> Generic4: ...
|
||||
|
||||
from some_module import PotentialTypeVar
|
||||
|
||||
class Generic5(list[PotentialTypeVar]):
|
||||
def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
def __enter__(self: Generic5) -> Generic5: ...
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import typing
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||
from enum import EnumMeta
|
||||
from typing import Any, overload
|
||||
from typing import Any, Generic, ParamSpec, Type, TypeVar, TypeVarTuple, overload
|
||||
|
||||
import typing_extensions
|
||||
from _typeshed import Self
|
||||
|
@ -215,3 +215,40 @@ def __imul__(self, other: Any) -> list[str]: ...
|
|||
|
||||
class UsesStringizedAnnotations:
|
||||
def __iadd__(self, other: "UsesStringizedAnnotations") -> "typing.Self": ...
|
||||
|
||||
|
||||
class NonGeneric1(tuple):
|
||||
def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
|
||||
class NonGeneric2(tuple):
|
||||
def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
|
||||
class Generic1[T](list):
|
||||
def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
def __enter__(self: Generic1) -> Generic1: ...
|
||||
|
||||
|
||||
### Correctness of typevar-likes are not verified.
|
||||
|
||||
T = TypeVar('T')
|
||||
P = ParamSpec()
|
||||
Ts = TypeVarTuple('foo')
|
||||
|
||||
class Generic2(Generic[T]):
|
||||
def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
def __enter__(self: Generic2) -> Generic2: ...
|
||||
|
||||
class Generic3(tuple[*Ts]):
|
||||
def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
def __enter__(self: Generic3) -> Generic3: ...
|
||||
|
||||
class Generic4(collections.abc.Callable[P, ...]):
|
||||
def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
def __enter__(self: Generic4) -> Generic4: ...
|
||||
|
||||
from some_module import PotentialTypeVar
|
||||
|
||||
class Generic5(list[PotentialTypeVar]):
|
||||
def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
def __enter__(self: Generic5) -> Generic5: ...
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use ruff_python_ast::{self as ast, Decorator, Expr, Parameters, Stmt};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use crate::settings::types::PythonVersion;
|
||||
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::helpers::map_subscript;
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_semantic::analyze;
|
||||
use ruff_python_semantic::analyze::class::might_be_generic;
|
||||
use ruff_python_semantic::analyze::visibility::{is_abstract, is_final, is_overload};
|
||||
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for methods that are annotated with a fixed return type which
|
||||
|
@ -71,6 +72,10 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
/// async def __aenter__(self) -> Self: ...
|
||||
/// def __iadd__(self, other: Foo) -> Self: ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe as it changes the meaning of your type annotations.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `typing.Self`](https://docs.python.org/3/library/typing.html#typing.Self)
|
||||
#[derive(ViolationMetadata)]
|
||||
|
@ -104,12 +109,12 @@ impl Violation for NonSelfReturnType {
|
|||
/// PYI034
|
||||
pub(crate) fn non_self_return_type(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
stmt: &ast::Stmt,
|
||||
is_async: bool,
|
||||
name: &str,
|
||||
decorator_list: &[Decorator],
|
||||
returns: Option<&Expr>,
|
||||
parameters: &Parameters,
|
||||
decorator_list: &[ast::Decorator],
|
||||
returns: Option<&ast::Expr>,
|
||||
parameters: &ast::Parameters,
|
||||
) {
|
||||
let semantic = checker.semantic();
|
||||
|
||||
|
@ -126,7 +131,7 @@ pub(crate) fn non_self_return_type(
|
|||
};
|
||||
|
||||
// PEP 673 forbids the use of `typing(_extensions).Self` in metaclasses.
|
||||
if analyze::class::is_metaclass(class_def, semantic).into() {
|
||||
if analyze::class::is_metaclass(class_def, semantic).is_yes() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -183,37 +188,11 @@ pub(crate) fn non_self_return_type(
|
|||
/// Add a diagnostic for the given method.
|
||||
fn add_diagnostic(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
returns: &Expr,
|
||||
stmt: &ast::Stmt,
|
||||
returns: &ast::Expr,
|
||||
class_def: &ast::StmtClassDef,
|
||||
method_name: &str,
|
||||
) {
|
||||
/// Return an [`Edit`] that imports `typing.Self` from `typing` or `typing_extensions`.
|
||||
fn import_self(checker: &Checker, range: TextRange) -> Option<Edit> {
|
||||
let target_version = checker.settings.target_version.as_tuple();
|
||||
let source_module = if target_version >= (3, 11) {
|
||||
"typing"
|
||||
} else {
|
||||
"typing_extensions"
|
||||
};
|
||||
|
||||
let (importer, semantic) = (checker.importer(), checker.semantic());
|
||||
let request = ImportRequest::import_from(source_module, "Self");
|
||||
|
||||
let (edit, ..) = importer
|
||||
.get_or_import_symbol(&request, range.start(), semantic)
|
||||
.ok()?;
|
||||
|
||||
Some(edit)
|
||||
}
|
||||
|
||||
/// Generate a [`Fix`] that replaces the return type with `Self`.
|
||||
fn replace_with_self(checker: &mut Checker, range: TextRange) -> Option<Fix> {
|
||||
let import_self = import_self(checker, range)?;
|
||||
let replace_with_self = Edit::range_replacement("Self".to_string(), range);
|
||||
Some(Fix::unsafe_edits(import_self, [replace_with_self]))
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
NonSelfReturnType {
|
||||
class_name: class_def.name.to_string(),
|
||||
|
@ -221,12 +200,72 @@ fn add_diagnostic(
|
|||
},
|
||||
stmt.identifier(),
|
||||
);
|
||||
if let Some(fix) = replace_with_self(checker, returns.range()) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
|
||||
diagnostic.try_set_fix(|| replace_with_self_fix(checker, stmt, returns, class_def));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
fn replace_with_self_fix(
|
||||
checker: &mut Checker,
|
||||
stmt: &ast::Stmt,
|
||||
returns: &ast::Expr,
|
||||
class_def: &ast::StmtClassDef,
|
||||
) -> anyhow::Result<Fix> {
|
||||
let semantic = checker.semantic();
|
||||
|
||||
let (self_import, self_binding) = {
|
||||
let source_module = if checker.settings.target_version >= PythonVersion::Py311 {
|
||||
"typing"
|
||||
} else {
|
||||
"typing_extensions"
|
||||
};
|
||||
|
||||
let (importer, semantic) = (checker.importer(), checker.semantic());
|
||||
let request = ImportRequest::import_from(source_module, "Self");
|
||||
importer.get_or_import_symbol(&request, returns.start(), semantic)?
|
||||
};
|
||||
|
||||
let mut others = Vec::with_capacity(2);
|
||||
|
||||
let remove_first_argument_type_hint = || -> Option<Edit> {
|
||||
let ast::StmtFunctionDef { parameters, .. } = stmt.as_function_def_stmt()?;
|
||||
let first = parameters.iter().next()?;
|
||||
let annotation = first.annotation()?;
|
||||
|
||||
is_class_reference(semantic, annotation, &class_def.name)
|
||||
.then(|| Edit::deletion(first.name().end(), annotation.end()))
|
||||
};
|
||||
|
||||
others.extend(remove_first_argument_type_hint());
|
||||
others.push(Edit::range_replacement(self_binding, returns.range()));
|
||||
|
||||
let applicability = if might_be_generic(class_def, checker.semantic()) {
|
||||
Applicability::DisplayOnly
|
||||
} else {
|
||||
Applicability::Unsafe
|
||||
};
|
||||
|
||||
Ok(Fix::applicable_edits(self_import, others, applicability))
|
||||
}
|
||||
|
||||
/// Return true if `annotation` is either `ClassName` or `type[ClassName]`
|
||||
fn is_class_reference(semantic: &SemanticModel, annotation: &ast::Expr, expected: &str) -> bool {
|
||||
if is_name(annotation, expected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let ast::Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = annotation else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !semantic.match_builtin_expr(value, "type") && !semantic.match_typing_expr(value, "Type") {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_name(slice, expected)
|
||||
}
|
||||
|
||||
/// Returns `true` if the method is an in-place binary operator.
|
||||
fn is_inplace_bin_op(name: &str) -> bool {
|
||||
matches!(
|
||||
|
@ -248,15 +287,15 @@ fn is_inplace_bin_op(name: &str) -> bool {
|
|||
}
|
||||
|
||||
/// Return `true` if the given expression resolves to the given name.
|
||||
fn is_name(expr: &Expr, name: &str) -> bool {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = expr else {
|
||||
fn is_name(expr: &ast::Expr, name: &str) -> bool {
|
||||
let ast::Expr::Name(ast::ExprName { id, .. }) = expr else {
|
||||
return false;
|
||||
};
|
||||
id.as_str() == name
|
||||
}
|
||||
|
||||
/// Return `true` if the given expression resolves to `typing.Self`.
|
||||
fn is_self(expr: &Expr, checker: &Checker) -> bool {
|
||||
fn is_self(expr: &ast::Expr, checker: &Checker) -> bool {
|
||||
checker.match_maybe_stringized_annotation(expr, |expr| {
|
||||
checker.semantic().match_typing_expr(expr, "Self")
|
||||
})
|
||||
|
@ -273,7 +312,7 @@ fn subclasses_iterator(class_def: &ast::StmtClassDef, semantic: &SemanticModel)
|
|||
}
|
||||
|
||||
/// Return `true` if the given expression resolves to `collections.abc.Iterable` or `collections.abc.Iterator`.
|
||||
fn is_iterable_or_iterator(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
fn is_iterable_or_iterator(expr: &ast::Expr, semantic: &SemanticModel) -> bool {
|
||||
semantic
|
||||
.resolve_qualified_name(map_subscript(expr))
|
||||
.is_some_and(|qualified_name| {
|
||||
|
@ -296,7 +335,7 @@ fn subclasses_async_iterator(class_def: &ast::StmtClassDef, semantic: &SemanticM
|
|||
}
|
||||
|
||||
/// Return `true` if the given expression resolves to `collections.abc.AsyncIterable` or `collections.abc.AsyncIterator`.
|
||||
fn is_async_iterable_or_iterator(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
fn is_async_iterable_or_iterator(expr: &ast::Expr, semantic: &SemanticModel) -> bool {
|
||||
semantic
|
||||
.resolve_qualified_name(map_subscript(expr))
|
||||
.is_some_and(|qualified_name| {
|
||||
|
|
|
@ -425,6 +425,8 @@ fn is_valid_default_value_without_annotation(default: &Expr) -> bool {
|
|||
|
||||
/// Returns `true` if an [`Expr`] appears to be `TypeVar`, `TypeVarTuple`, `NewType`, or `ParamSpec`
|
||||
/// call.
|
||||
///
|
||||
/// See also [`ruff_python_semantic::analyze::typing::TypeVarLikeChecker::is_type_var_like_call`].
|
||||
fn is_type_var_like_call(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
|
||||
return false;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PYI034.py:21:9: PYI034 [*] `__new__` methods in classes like `Bad` usually return `self` at runtime
|
||||
|
|
||||
|
@ -17,7 +16,7 @@ PYI034.py:21:9: PYI034 [*] `__new__` methods in classes like `Bad` usually retur
|
|||
19 19 | object
|
||||
20 20 | ): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||
21 |- def __new__(cls, *args: Any, **kwargs: Any) -> Bad:
|
||||
21 |+ def __new__(cls, *args: Any, **kwargs: Any) -> Self:
|
||||
21 |+ def __new__(cls, *args: Any, **kwargs: Any) -> typing.Self:
|
||||
22 22 | ... # 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: ..."
|
||||
23 23 |
|
||||
24 24 | def __repr__(self) -> str:
|
||||
|
@ -37,7 +36,7 @@ PYI034.py:36:9: PYI034 [*] `__enter__` methods in classes like `Bad` usually ret
|
|||
34 34 | ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
|
||||
35 35 |
|
||||
36 |- def __enter__(self) -> Bad:
|
||||
36 |+ def __enter__(self) -> Self:
|
||||
36 |+ def __enter__(self) -> typing.Self:
|
||||
37 37 | ... # 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: ..."
|
||||
38 38 |
|
||||
39 39 | async def __aenter__(self) -> Bad:
|
||||
|
@ -57,7 +56,7 @@ PYI034.py:39:15: PYI034 [*] `__aenter__` methods in classes like `Bad` usually r
|
|||
37 37 | ... # 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: ..."
|
||||
38 38 |
|
||||
39 |- async def __aenter__(self) -> Bad:
|
||||
39 |+ async def __aenter__(self) -> Self:
|
||||
39 |+ async def __aenter__(self) -> typing.Self:
|
||||
40 40 | ... # 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: ..."
|
||||
41 41 |
|
||||
42 42 | def __iadd__(self, other: Bad) -> Bad:
|
||||
|
@ -77,7 +76,7 @@ PYI034.py:42:9: PYI034 [*] `__iadd__` methods in classes like `Bad` usually retu
|
|||
40 40 | ... # 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: ..."
|
||||
41 41 |
|
||||
42 |- def __iadd__(self, other: Bad) -> Bad:
|
||||
42 |+ def __iadd__(self, other: Bad) -> Self:
|
||||
42 |+ def __iadd__(self, other: Bad) -> typing.Self:
|
||||
43 43 | ... # 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: ..."
|
||||
44 44 |
|
||||
45 45 |
|
||||
|
@ -96,7 +95,7 @@ PYI034.py:165:9: PYI034 [*] `__iter__` methods in classes like `BadIterator1` us
|
|||
163 163 |
|
||||
164 164 | class BadIterator1(Iterator[int]):
|
||||
165 |- def __iter__(self) -> Iterator[int]:
|
||||
165 |+ def __iter__(self) -> Self:
|
||||
165 |+ def __iter__(self) -> typing.Self:
|
||||
166 166 | ... # 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: ..."
|
||||
167 167 |
|
||||
168 168 |
|
||||
|
@ -116,7 +115,7 @@ PYI034.py:172:9: PYI034 [*] `__iter__` methods in classes like `BadIterator2` us
|
|||
170 170 | typing.Iterator[int]
|
||||
171 171 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||
172 |- def __iter__(self) -> Iterator[int]:
|
||||
172 |+ def __iter__(self) -> Self:
|
||||
172 |+ def __iter__(self) -> typing.Self:
|
||||
173 173 | ... # 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: ..."
|
||||
174 174 |
|
||||
175 175 |
|
||||
|
@ -136,7 +135,7 @@ PYI034.py:179:9: PYI034 [*] `__iter__` methods in classes like `BadIterator3` us
|
|||
177 177 | typing.Iterator[int]
|
||||
178 178 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||
179 |- def __iter__(self) -> collections.abc.Iterator[int]:
|
||||
179 |+ def __iter__(self) -> Self:
|
||||
179 |+ def __iter__(self) -> typing.Self:
|
||||
180 180 | ... # 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: ..."
|
||||
181 181 |
|
||||
182 182 |
|
||||
|
@ -156,7 +155,7 @@ PYI034.py:185:9: PYI034 [*] `__iter__` methods in classes like `BadIterator4` us
|
|||
183 183 | class BadIterator4(Iterator[int]):
|
||||
184 184 | # Note: *Iterable*, not *Iterator*, returned!
|
||||
185 |- def __iter__(self) -> Iterable[int]:
|
||||
185 |+ def __iter__(self) -> Self:
|
||||
185 |+ def __iter__(self) -> typing.Self:
|
||||
186 186 | ... # 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: ..."
|
||||
187 187 |
|
||||
188 188 |
|
||||
|
@ -175,7 +174,7 @@ PYI034.py:195:9: PYI034 [*] `__aiter__` methods in classes like `BadAsyncIterato
|
|||
193 193 |
|
||||
194 194 | class BadAsyncIterator(collections.abc.AsyncIterator[str]):
|
||||
195 |- def __aiter__(self) -> typing.AsyncIterator[str]:
|
||||
195 |+ def __aiter__(self) -> Self:
|
||||
195 |+ def __aiter__(self) -> typing.Self:
|
||||
196 196 | ... # 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)
|
||||
197 197 |
|
||||
198 198 | class SubclassOfBadIterator3(BadIterator3):
|
||||
|
@ -194,7 +193,7 @@ PYI034.py:199:9: PYI034 [*] `__iter__` methods in classes like `SubclassOfBadIte
|
|||
197 197 |
|
||||
198 198 | class SubclassOfBadIterator3(BadIterator3):
|
||||
199 |- def __iter__(self) -> Iterator[int]: # Y034
|
||||
199 |+ def __iter__(self) -> Self: # Y034
|
||||
199 |+ def __iter__(self) -> typing.Self: # Y034
|
||||
200 200 | ...
|
||||
201 201 |
|
||||
202 202 | class SubclassOfBadAsyncIterator(BadAsyncIterator):
|
||||
|
@ -213,7 +212,260 @@ PYI034.py:203:9: PYI034 [*] `__aiter__` methods in classes like `SubclassOfBadAs
|
|||
201 201 |
|
||||
202 202 | class SubclassOfBadAsyncIterator(BadAsyncIterator):
|
||||
203 |- def __aiter__(self) -> collections.abc.AsyncIterator[str]: # Y034
|
||||
203 |+ def __aiter__(self) -> Self: # Y034
|
||||
203 |+ def __aiter__(self) -> typing.Self: # Y034
|
||||
204 204 | ...
|
||||
205 205 |
|
||||
206 206 | class AsyncIteratorReturningAsyncIterable:
|
||||
|
||||
PYI034.py:327:9: PYI034 [*] `__new__` methods in classes like `NonGeneric1` usually return `self` at runtime
|
||||
|
|
||||
326 | class NonGeneric1(tuple):
|
||||
327 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
| ^^^^^^^ PYI034
|
||||
328 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
324 324 |
|
||||
325 325 |
|
||||
326 326 | class NonGeneric1(tuple):
|
||||
327 |- def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
327 |+ def __new__(cls, *args, **kwargs) -> typing.Self: ...
|
||||
328 328 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
329 329 |
|
||||
330 330 | class NonGeneric2(tuple):
|
||||
|
||||
PYI034.py:328:9: PYI034 [*] `__enter__` methods in classes like `NonGeneric1` usually return `self` at runtime
|
||||
|
|
||||
326 | class NonGeneric1(tuple):
|
||||
327 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
328 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
329 |
|
||||
330 | class NonGeneric2(tuple):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
325 325 |
|
||||
326 326 | class NonGeneric1(tuple):
|
||||
327 327 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
328 |- def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
328 |+ def __enter__(self) -> typing.Self: ...
|
||||
329 329 |
|
||||
330 330 | class NonGeneric2(tuple):
|
||||
331 331 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
|
||||
PYI034.py:331:9: PYI034 [*] `__new__` methods in classes like `NonGeneric2` usually return `self` at runtime
|
||||
|
|
||||
330 | class NonGeneric2(tuple):
|
||||
331 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
| ^^^^^^^ PYI034
|
||||
332 |
|
||||
333 | class Generic1[T](list):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
328 328 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
329 329 |
|
||||
330 330 | class NonGeneric2(tuple):
|
||||
331 |- def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
331 |+ def __new__(cls) -> typing.Self: ...
|
||||
332 332 |
|
||||
333 333 | class Generic1[T](list):
|
||||
334 334 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
|
||||
PYI034.py:334:9: PYI034 `__new__` methods in classes like `Generic1` usually return `self` at runtime
|
||||
|
|
||||
333 | class Generic1[T](list):
|
||||
334 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
| ^^^^^^^ PYI034
|
||||
335 | def __enter__(self: Generic1) -> Generic1: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
331 331 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
332 332 |
|
||||
333 333 | class Generic1[T](list):
|
||||
334 |- def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
334 |+ def __new__(cls) -> typing.Self: ...
|
||||
335 335 | def __enter__(self: Generic1) -> Generic1: ...
|
||||
336 336 |
|
||||
337 337 |
|
||||
|
||||
PYI034.py:335:9: PYI034 `__enter__` methods in classes like `Generic1` usually return `self` at runtime
|
||||
|
|
||||
333 | class Generic1[T](list):
|
||||
334 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
335 | def __enter__(self: Generic1) -> Generic1: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
332 332 |
|
||||
333 333 | class Generic1[T](list):
|
||||
334 334 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
335 |- def __enter__(self: Generic1) -> Generic1: ...
|
||||
335 |+ def __enter__(self) -> typing.Self: ...
|
||||
336 336 |
|
||||
337 337 |
|
||||
338 338 | ### Correctness of typevar-likes are not verified.
|
||||
|
||||
PYI034.py:345:9: PYI034 `__new__` methods in classes like `Generic2` usually return `self` at runtime
|
||||
|
|
||||
344 | class Generic2(Generic[T]):
|
||||
345 | def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
| ^^^^^^^ PYI034
|
||||
346 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
342 342 | Ts = TypeVarTuple('foo')
|
||||
343 343 |
|
||||
344 344 | class Generic2(Generic[T]):
|
||||
345 |- def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
345 |+ def __new__(cls) -> typing.Self: ...
|
||||
346 346 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
347 347 |
|
||||
348 348 | class Generic3(tuple[*Ts]):
|
||||
|
||||
PYI034.py:346:9: PYI034 `__enter__` methods in classes like `Generic2` usually return `self` at runtime
|
||||
|
|
||||
344 | class Generic2(Generic[T]):
|
||||
345 | def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
346 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
347 |
|
||||
348 | class Generic3(tuple[*Ts]):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
343 343 |
|
||||
344 344 | class Generic2(Generic[T]):
|
||||
345 345 | def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
346 |- def __enter__(self: Generic2) -> Generic2: ...
|
||||
346 |+ def __enter__(self) -> typing.Self: ...
|
||||
347 347 |
|
||||
348 348 | class Generic3(tuple[*Ts]):
|
||||
349 349 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
|
||||
PYI034.py:349:9: PYI034 `__new__` methods in classes like `Generic3` usually return `self` at runtime
|
||||
|
|
||||
348 | class Generic3(tuple[*Ts]):
|
||||
349 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
| ^^^^^^^ PYI034
|
||||
350 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
346 346 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
347 347 |
|
||||
348 348 | class Generic3(tuple[*Ts]):
|
||||
349 |- def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
349 |+ def __new__(cls) -> typing.Self: ...
|
||||
350 350 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
351 351 |
|
||||
352 352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
|
||||
PYI034.py:350:9: PYI034 `__enter__` methods in classes like `Generic3` usually return `self` at runtime
|
||||
|
|
||||
348 | class Generic3(tuple[*Ts]):
|
||||
349 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
350 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
351 |
|
||||
352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
347 347 |
|
||||
348 348 | class Generic3(tuple[*Ts]):
|
||||
349 349 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
350 |- def __enter__(self: Generic3) -> Generic3: ...
|
||||
350 |+ def __enter__(self) -> typing.Self: ...
|
||||
351 351 |
|
||||
352 352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
353 353 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
|
||||
PYI034.py:353:9: PYI034 `__new__` methods in classes like `Generic4` usually return `self` at runtime
|
||||
|
|
||||
352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
353 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
| ^^^^^^^ PYI034
|
||||
354 | def __enter__(self: Generic4) -> Generic4: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
350 350 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
351 351 |
|
||||
352 352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
353 |- def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
353 |+ def __new__(cls) -> typing.Self: ...
|
||||
354 354 | def __enter__(self: Generic4) -> Generic4: ...
|
||||
355 355 |
|
||||
356 356 | from some_module import PotentialTypeVar
|
||||
|
||||
PYI034.py:354:9: PYI034 `__enter__` methods in classes like `Generic4` usually return `self` at runtime
|
||||
|
|
||||
352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
353 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
354 | def __enter__(self: Generic4) -> Generic4: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
355 |
|
||||
356 | from some_module import PotentialTypeVar
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
351 351 |
|
||||
352 352 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
353 353 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
354 |- def __enter__(self: Generic4) -> Generic4: ...
|
||||
354 |+ def __enter__(self) -> typing.Self: ...
|
||||
355 355 |
|
||||
356 356 | from some_module import PotentialTypeVar
|
||||
357 357 |
|
||||
|
||||
PYI034.py:359:9: PYI034 [*] `__new__` methods in classes like `Generic5` usually return `self` at runtime
|
||||
|
|
||||
358 | class Generic5(list[PotentialTypeVar]):
|
||||
359 | def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
| ^^^^^^^ PYI034
|
||||
360 | def __enter__(self: Generic5) -> Generic5: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
356 356 | from some_module import PotentialTypeVar
|
||||
357 357 |
|
||||
358 358 | class Generic5(list[PotentialTypeVar]):
|
||||
359 |- def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
359 |+ def __new__(cls) -> typing.Self: ...
|
||||
360 360 | def __enter__(self: Generic5) -> Generic5: ...
|
||||
361 361 |
|
||||
|
||||
PYI034.py:360:9: PYI034 [*] `__enter__` methods in classes like `Generic5` usually return `self` at runtime
|
||||
|
|
||||
358 | class Generic5(list[PotentialTypeVar]):
|
||||
359 | def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
360 | def __enter__(self: Generic5) -> Generic5: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
357 357 |
|
||||
358 358 | class Generic5(list[PotentialTypeVar]):
|
||||
359 359 | def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
360 |- def __enter__(self: Generic5) -> Generic5: ...
|
||||
360 |+ def __enter__(self) -> typing.Self: ...
|
||||
361 361 |
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
PYI034.pyi:20:9: PYI034 [*] `__new__` methods in classes like `Bad` usually return `self` at runtime
|
||||
|
|
||||
|
@ -18,7 +17,7 @@ PYI034.pyi:20:9: PYI034 [*] `__new__` methods in classes like `Bad` usually retu
|
|||
20 20 | def __new__(
|
||||
21 21 | cls, *args: Any, **kwargs: Any
|
||||
22 |- ) -> Bad: ... # 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: ..."
|
||||
22 |+ ) -> Self: ... # 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: ..."
|
||||
22 |+ ) -> typing.Self: ... # 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: ..."
|
||||
23 23 | def __repr__(
|
||||
24 24 | self,
|
||||
25 25 | ) -> str: ... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||
|
@ -39,7 +38,7 @@ PYI034.pyi:35:9: PYI034 [*] `__enter__` methods in classes like `Bad` usually re
|
|||
35 35 | def __enter__(
|
||||
36 36 | self,
|
||||
37 |- ) -> Bad: ... # 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: ..."
|
||||
37 |+ ) -> Self: ... # 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: ..."
|
||||
37 |+ ) -> typing.Self: ... # 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: ..."
|
||||
38 38 | async def __aenter__(
|
||||
39 39 | self,
|
||||
40 40 | ) -> Bad: ... # 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: ..."
|
||||
|
@ -60,7 +59,7 @@ PYI034.pyi:38:15: PYI034 [*] `__aenter__` methods in classes like `Bad` usually
|
|||
38 38 | async def __aenter__(
|
||||
39 39 | self,
|
||||
40 |- ) -> Bad: ... # 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: ..."
|
||||
40 |+ ) -> Self: ... # 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: ..."
|
||||
40 |+ ) -> typing.Self: ... # 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: ..."
|
||||
41 41 | def __iadd__(
|
||||
42 42 | self, other: Bad
|
||||
43 43 | ) -> Bad: ... # 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: ..."
|
||||
|
@ -81,7 +80,7 @@ PYI034.pyi:41:9: PYI034 [*] `__iadd__` methods in classes like `Bad` usually ret
|
|||
41 41 | def __iadd__(
|
||||
42 42 | self, other: Bad
|
||||
43 |- ) -> Bad: ... # 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: ..."
|
||||
43 |+ ) -> Self: ... # 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: ..."
|
||||
43 |+ ) -> typing.Self: ... # 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: ..."
|
||||
44 44 |
|
||||
45 45 | class AlsoBad(
|
||||
46 46 | int, builtins.object
|
||||
|
@ -103,7 +102,7 @@ PYI034.pyi:104:9: PYI034 [*] `__iter__` methods in classes like `BadIterator1` u
|
|||
106 |- ) -> Iterator[
|
||||
107 |- int
|
||||
108 |- ]: ... # 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: ..."
|
||||
106 |+ ) -> Self: ... # 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: ..."
|
||||
106 |+ ) -> typing.Self: ... # 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: ..."
|
||||
109 107 |
|
||||
110 108 | class BadIterator2(
|
||||
111 109 | typing.Iterator[int]
|
||||
|
@ -126,7 +125,7 @@ PYI034.pyi:113:9: PYI034 [*] `__iter__` methods in classes like `BadIterator2` u
|
|||
115 |- ) -> Iterator[
|
||||
116 |- int
|
||||
117 |- ]: ... # 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: ..."
|
||||
115 |+ ) -> Self: ... # 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: ..."
|
||||
115 |+ ) -> typing.Self: ... # 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: ..."
|
||||
118 116 |
|
||||
119 117 | class BadIterator3(
|
||||
120 118 | typing.Iterator[int]
|
||||
|
@ -149,7 +148,7 @@ PYI034.pyi:122:9: PYI034 [*] `__iter__` methods in classes like `BadIterator3` u
|
|||
124 |- ) -> collections.abc.Iterator[
|
||||
125 |- int
|
||||
126 |- ]: ... # 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: ..."
|
||||
124 |+ ) -> Self: ... # 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: ..."
|
||||
124 |+ ) -> typing.Self: ... # 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: ..."
|
||||
127 125 |
|
||||
128 126 | class BadIterator4(Iterator[int]):
|
||||
129 127 | # Note: *Iterable*, not *Iterator*, returned!
|
||||
|
@ -172,7 +171,7 @@ PYI034.pyi:130:9: PYI034 [*] `__iter__` methods in classes like `BadIterator4` u
|
|||
132 |- ) -> Iterable[
|
||||
133 |- int
|
||||
134 |- ]: ... # 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: ..."
|
||||
132 |+ ) -> Self: ... # 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: ..."
|
||||
132 |+ ) -> typing.Self: ... # 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: ..."
|
||||
135 133 |
|
||||
136 134 | class IteratorReturningIterable:
|
||||
137 135 | def __iter__(
|
||||
|
@ -194,7 +193,258 @@ PYI034.pyi:144:9: PYI034 [*] `__aiter__` methods in classes like `BadAsyncIterat
|
|||
146 |- ) -> typing.AsyncIterator[
|
||||
147 |- str
|
||||
148 |- ]: ... # 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)
|
||||
146 |+ ) -> Self: ... # 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)
|
||||
146 |+ ) -> typing.Self: ... # 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)
|
||||
149 147 |
|
||||
150 148 | class AsyncIteratorReturningAsyncIterable:
|
||||
151 149 | def __aiter__(
|
||||
|
||||
PYI034.pyi:221:9: PYI034 [*] `__new__` methods in classes like `NonGeneric1` usually return `self` at runtime
|
||||
|
|
||||
220 | class NonGeneric1(tuple):
|
||||
221 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
| ^^^^^^^ PYI034
|
||||
222 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
218 218 |
|
||||
219 219 |
|
||||
220 220 | class NonGeneric1(tuple):
|
||||
221 |- def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
221 |+ def __new__(cls, *args, **kwargs) -> typing.Self: ...
|
||||
222 222 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
223 223 |
|
||||
224 224 | class NonGeneric2(tuple):
|
||||
|
||||
PYI034.pyi:222:9: PYI034 [*] `__enter__` methods in classes like `NonGeneric1` usually return `self` at runtime
|
||||
|
|
||||
220 | class NonGeneric1(tuple):
|
||||
221 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
222 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
223 |
|
||||
224 | class NonGeneric2(tuple):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
219 219 |
|
||||
220 220 | class NonGeneric1(tuple):
|
||||
221 221 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
|
||||
222 |- def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
222 |+ def __enter__(self) -> typing.Self: ...
|
||||
223 223 |
|
||||
224 224 | class NonGeneric2(tuple):
|
||||
225 225 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
|
||||
PYI034.pyi:225:9: PYI034 [*] `__new__` methods in classes like `NonGeneric2` usually return `self` at runtime
|
||||
|
|
||||
224 | class NonGeneric2(tuple):
|
||||
225 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
| ^^^^^^^ PYI034
|
||||
226 |
|
||||
227 | class Generic1[T](list):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
222 222 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
|
||||
223 223 |
|
||||
224 224 | class NonGeneric2(tuple):
|
||||
225 |- def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
225 |+ def __new__(cls) -> typing.Self: ...
|
||||
226 226 |
|
||||
227 227 | class Generic1[T](list):
|
||||
228 228 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
|
||||
PYI034.pyi:228:9: PYI034 `__new__` methods in classes like `Generic1` usually return `self` at runtime
|
||||
|
|
||||
227 | class Generic1[T](list):
|
||||
228 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
| ^^^^^^^ PYI034
|
||||
229 | def __enter__(self: Generic1) -> Generic1: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
225 225 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
|
||||
226 226 |
|
||||
227 227 | class Generic1[T](list):
|
||||
228 |- def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
228 |+ def __new__(cls) -> typing.Self: ...
|
||||
229 229 | def __enter__(self: Generic1) -> Generic1: ...
|
||||
230 230 |
|
||||
231 231 |
|
||||
|
||||
PYI034.pyi:229:9: PYI034 `__enter__` methods in classes like `Generic1` usually return `self` at runtime
|
||||
|
|
||||
227 | class Generic1[T](list):
|
||||
228 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
229 | def __enter__(self: Generic1) -> Generic1: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
226 226 |
|
||||
227 227 | class Generic1[T](list):
|
||||
228 228 | def __new__(cls: type[Generic1]) -> Generic1: ...
|
||||
229 |- def __enter__(self: Generic1) -> Generic1: ...
|
||||
229 |+ def __enter__(self) -> typing.Self: ...
|
||||
230 230 |
|
||||
231 231 |
|
||||
232 232 | ### Correctness of typevar-likes are not verified.
|
||||
|
||||
PYI034.pyi:239:9: PYI034 `__new__` methods in classes like `Generic2` usually return `self` at runtime
|
||||
|
|
||||
238 | class Generic2(Generic[T]):
|
||||
239 | def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
| ^^^^^^^ PYI034
|
||||
240 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
236 236 | Ts = TypeVarTuple('foo')
|
||||
237 237 |
|
||||
238 238 | class Generic2(Generic[T]):
|
||||
239 |- def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
239 |+ def __new__(cls) -> typing.Self: ...
|
||||
240 240 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
241 241 |
|
||||
242 242 | class Generic3(tuple[*Ts]):
|
||||
|
||||
PYI034.pyi:240:9: PYI034 `__enter__` methods in classes like `Generic2` usually return `self` at runtime
|
||||
|
|
||||
238 | class Generic2(Generic[T]):
|
||||
239 | def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
240 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
241 |
|
||||
242 | class Generic3(tuple[*Ts]):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
237 237 |
|
||||
238 238 | class Generic2(Generic[T]):
|
||||
239 239 | def __new__(cls: type[Generic2]) -> Generic2: ...
|
||||
240 |- def __enter__(self: Generic2) -> Generic2: ...
|
||||
240 |+ def __enter__(self) -> typing.Self: ...
|
||||
241 241 |
|
||||
242 242 | class Generic3(tuple[*Ts]):
|
||||
243 243 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
|
||||
PYI034.pyi:243:9: PYI034 `__new__` methods in classes like `Generic3` usually return `self` at runtime
|
||||
|
|
||||
242 | class Generic3(tuple[*Ts]):
|
||||
243 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
| ^^^^^^^ PYI034
|
||||
244 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
240 240 | def __enter__(self: Generic2) -> Generic2: ...
|
||||
241 241 |
|
||||
242 242 | class Generic3(tuple[*Ts]):
|
||||
243 |- def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
243 |+ def __new__(cls) -> typing.Self: ...
|
||||
244 244 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
245 245 |
|
||||
246 246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
|
||||
PYI034.pyi:244:9: PYI034 `__enter__` methods in classes like `Generic3` usually return `self` at runtime
|
||||
|
|
||||
242 | class Generic3(tuple[*Ts]):
|
||||
243 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
244 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
245 |
|
||||
246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
241 241 |
|
||||
242 242 | class Generic3(tuple[*Ts]):
|
||||
243 243 | def __new__(cls: type[Generic3]) -> Generic3: ...
|
||||
244 |- def __enter__(self: Generic3) -> Generic3: ...
|
||||
244 |+ def __enter__(self) -> typing.Self: ...
|
||||
245 245 |
|
||||
246 246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
247 247 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
|
||||
PYI034.pyi:247:9: PYI034 `__new__` methods in classes like `Generic4` usually return `self` at runtime
|
||||
|
|
||||
246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
247 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
| ^^^^^^^ PYI034
|
||||
248 | def __enter__(self: Generic4) -> Generic4: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
244 244 | def __enter__(self: Generic3) -> Generic3: ...
|
||||
245 245 |
|
||||
246 246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
247 |- def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
247 |+ def __new__(cls) -> typing.Self: ...
|
||||
248 248 | def __enter__(self: Generic4) -> Generic4: ...
|
||||
249 249 |
|
||||
250 250 | from some_module import PotentialTypeVar
|
||||
|
||||
PYI034.pyi:248:9: PYI034 `__enter__` methods in classes like `Generic4` usually return `self` at runtime
|
||||
|
|
||||
246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
247 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
248 | def __enter__(self: Generic4) -> Generic4: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
249 |
|
||||
250 | from some_module import PotentialTypeVar
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
245 245 |
|
||||
246 246 | class Generic4(collections.abc.Callable[P, ...]):
|
||||
247 247 | def __new__(cls: type[Generic4]) -> Generic4: ...
|
||||
248 |- def __enter__(self: Generic4) -> Generic4: ...
|
||||
248 |+ def __enter__(self) -> typing.Self: ...
|
||||
249 249 |
|
||||
250 250 | from some_module import PotentialTypeVar
|
||||
251 251 |
|
||||
|
||||
PYI034.pyi:253:9: PYI034 `__new__` methods in classes like `Generic5` usually return `self` at runtime
|
||||
|
|
||||
252 | class Generic5(list[PotentialTypeVar]):
|
||||
253 | def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
| ^^^^^^^ PYI034
|
||||
254 | def __enter__(self: Generic5) -> Generic5: ...
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
250 250 | from some_module import PotentialTypeVar
|
||||
251 251 |
|
||||
252 252 | class Generic5(list[PotentialTypeVar]):
|
||||
253 |- def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
253 |+ def __new__(cls) -> typing.Self: ...
|
||||
254 254 | def __enter__(self: Generic5) -> Generic5: ...
|
||||
|
||||
PYI034.pyi:254:9: PYI034 `__enter__` methods in classes like `Generic5` usually return `self` at runtime
|
||||
|
|
||||
252 | class Generic5(list[PotentialTypeVar]):
|
||||
253 | def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
254 | def __enter__(self: Generic5) -> Generic5: ...
|
||||
| ^^^^^^^^^ PYI034
|
||||
|
|
||||
= help: Use `Self` as return type
|
||||
|
||||
ℹ Display-only fix
|
||||
251 251 |
|
||||
252 252 | class Generic5(list[PotentialTypeVar]):
|
||||
253 253 | def __new__(cls: type[Generic5]) -> Generic5: ...
|
||||
254 |- def __enter__(self: Generic5) -> Generic5: ...
|
||||
254 |+ def __enter__(self) -> typing.Self: ...
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::analyze::typing;
|
||||
use crate::{BindingId, SemanticModel};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::helpers::map_subscript;
|
||||
use ruff_python_ast::name::QualifiedName;
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_python_ast::{Expr, ExprName, ExprStarred, ExprSubscript, ExprTuple};
|
||||
|
||||
/// Return `true` if any base class matches a [`QualifiedName`] predicate.
|
||||
pub fn any_qualified_base_class(
|
||||
|
@ -129,9 +130,9 @@ pub enum IsMetaclass {
|
|||
Maybe,
|
||||
}
|
||||
|
||||
impl From<IsMetaclass> for bool {
|
||||
fn from(value: IsMetaclass) -> Self {
|
||||
matches!(value, IsMetaclass::Yes)
|
||||
impl IsMetaclass {
|
||||
pub const fn is_yes(self) -> bool {
|
||||
matches!(self, IsMetaclass::Yes)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,3 +171,57 @@ pub fn is_metaclass(class_def: &ast::StmtClassDef, semantic: &SemanticModel) ->
|
|||
(false, _) => IsMetaclass::No,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a class might generic.
|
||||
///
|
||||
/// A class is considered generic if at least one of its direct bases
|
||||
/// is subscripted with a `TypeVar`-like,
|
||||
/// or if it is defined using PEP 695 syntax.
|
||||
///
|
||||
/// Therefore, a class *might* be generic if it uses PEP-695 syntax
|
||||
/// or at least one of its direct bases is a subscript expression that
|
||||
/// is subscripted with an object that *might* be a `TypeVar`-like.
|
||||
pub fn might_be_generic(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
|
||||
if class_def.type_params.is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
class_def.bases().iter().any(|base| {
|
||||
let Expr::Subscript(ExprSubscript { slice, .. }) = base else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Expr::Tuple(ExprTuple { elts, .. }) = slice.as_ref() else {
|
||||
return expr_might_be_typevar_like(slice, semantic);
|
||||
};
|
||||
|
||||
elts.iter()
|
||||
.any(|elt| expr_might_be_typevar_like(elt, semantic))
|
||||
})
|
||||
}
|
||||
|
||||
fn expr_might_be_typevar_like(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
is_known_typevar(expr, semantic) || expr_might_be_old_style_typevar_like(expr, semantic)
|
||||
}
|
||||
|
||||
fn is_known_typevar(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
semantic.match_typing_expr(expr, "AnyStr")
|
||||
}
|
||||
|
||||
fn expr_might_be_old_style_typevar_like(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
match expr {
|
||||
Expr::Attribute(..) => true,
|
||||
Expr::Name(name) => might_be_old_style_typevar_like(name, semantic),
|
||||
Expr::Starred(ExprStarred { value, .. }) => {
|
||||
expr_might_be_old_style_typevar_like(value, semantic)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn might_be_old_style_typevar_like(name: &ExprName, semantic: &SemanticModel) -> bool {
|
||||
let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else {
|
||||
return !semantic.has_builtin_binding(&name.id);
|
||||
};
|
||||
typing::is_type_var_like(binding, semantic)
|
||||
}
|
||||
|
|
|
@ -778,6 +778,40 @@ impl TypeChecker for PathlibPathChecker {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct TypeVarLikeChecker;
|
||||
|
||||
impl TypeVarLikeChecker {
|
||||
/// Returns `true` if an [`Expr`] is a `TypeVar`, `TypeVarTuple`, or `ParamSpec` call.
|
||||
///
|
||||
/// See also [`ruff_linter::rules::flake8_pyi::rules::simple_defaults::is_type_var_like_call`].
|
||||
fn is_type_var_like_call(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
|
||||
return false;
|
||||
};
|
||||
let Some(qualified_name) = semantic.resolve_qualified_name(func) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
matches!(
|
||||
qualified_name.segments(),
|
||||
[
|
||||
"typing" | "typing_extensions",
|
||||
"TypeVar" | "TypeVarTuple" | "ParamSpec"
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeChecker for TypeVarLikeChecker {
|
||||
fn match_annotation(_annotation: &Expr, _semantic: &SemanticModel) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn match_initializer(initializer: &Expr, semantic: &SemanticModel) -> bool {
|
||||
Self::is_type_var_like_call(initializer, semantic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given binding can be considered a list.
|
||||
///
|
||||
/// For this, we check what value might be associated with it through it's initialization and
|
||||
|
@ -867,6 +901,11 @@ pub fn is_pathlib_path(binding: &Binding, semantic: &SemanticModel) -> bool {
|
|||
check_type::<PathlibPathChecker>(binding, semantic)
|
||||
}
|
||||
|
||||
/// Test whether the given binding is for an old-style `TypeVar`, `TypeVarTuple` or a `ParamSpec`.
|
||||
pub fn is_type_var_like(binding: &Binding, semantic: &SemanticModel) -> bool {
|
||||
check_type::<TypeVarLikeChecker>(binding, semantic)
|
||||
}
|
||||
|
||||
/// Find the [`ParameterWithDefault`] corresponding to the given [`Binding`].
|
||||
#[inline]
|
||||
fn find_parameter<'a>(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue