[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:
InSync 2024-12-09 21:59:12 +07:00 committed by GitHub
parent 0e9427255f
commit aa6b812a73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 785 additions and 73 deletions

View file

@ -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: ...

View file

@ -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: ...

View file

@ -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| {

View file

@ -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;

View file

@ -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 |

View file

@ -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: ...

View file

@ -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)
}

View file

@ -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>(