mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:48 +00:00
Add PYI034 for flake8-pyi
plugin (#4764)
This commit is contained in:
parent
c68686b1de
commit
fcbf5c3fae
14 changed files with 918 additions and 2 deletions
280
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.py
vendored
Normal file
280
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.py
vendored
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
# flags: --extend-ignore=Y023
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import builtins
|
||||||
|
import collections.abc
|
||||||
|
import typing
|
||||||
|
from abc import abstractmethod
|
||||||
|
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||||
|
from typing import Any, overload
|
||||||
|
|
||||||
|
import typing_extensions
|
||||||
|
from _typeshed import Self
|
||||||
|
from typing_extensions import final
|
||||||
|
|
||||||
|
|
||||||
|
class Bad(
|
||||||
|
object
|
||||||
|
): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> 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: ..."
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||||
|
|
||||||
|
def __str__(self) -> builtins.str:
|
||||||
|
... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
... # Y032 Prefer "object" to "Any" for the second parameter in "__eq__" methods
|
||||||
|
|
||||||
|
def __ne__(self, other: typing.Any) -> typing.Any:
|
||||||
|
... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
|
||||||
|
|
||||||
|
def __enter__(self) -> 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: ..."
|
||||||
|
|
||||||
|
async def __aenter__(self) -> 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: ..."
|
||||||
|
|
||||||
|
def __iadd__(self, other: Bad) -> 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: ..."
|
||||||
|
|
||||||
|
|
||||||
|
class AlsoBad(int, builtins.object):
|
||||||
|
... # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||||
|
|
||||||
|
|
||||||
|
class Good:
|
||||||
|
def __new__(cls: type[Self], *args: Any, **kwargs: Any) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __str__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __ne__(self, obj: object) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __enter__(self: Self) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def __aenter__(self: Self) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __ior__(self: Self, other: Self) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Fine:
|
||||||
|
@overload
|
||||||
|
def __new__(cls, foo: int) -> FineSubclass:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> Fine:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __str__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __eq__(self, other: Any, strange_extra_arg: list[str]) -> Any:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __ne__(self, *, kw_only_other: Any) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __enter__(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def __aenter__(self) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class FineSubclass(Fine):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class StrangeButAcceptable(str):
|
||||||
|
@typing_extensions.overload
|
||||||
|
def __new__(cls, foo: int) -> StrangeButAcceptableSubclass:
|
||||||
|
...
|
||||||
|
|
||||||
|
@typing_extensions.overload
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> StrangeButAcceptable:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __str__(self) -> StrangeButAcceptable:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self) -> StrangeButAcceptable:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class StrangeButAcceptableSubclass(StrangeButAcceptable):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class FineAndDandy:
|
||||||
|
def __str__(self, weird_extra_arg) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self, weird_extra_arg_with_default=...) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
|
class WillNotBeSubclassed:
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> WillNotBeSubclassed:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __enter__(self) -> WillNotBeSubclassed:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def __aenter__(self) -> WillNotBeSubclassed:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# we don't emit an error for these; out of scope for a linter
|
||||||
|
class InvalidButPluginDoesNotCrash:
|
||||||
|
def __new__() -> InvalidButPluginDoesNotCrash:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __enter__() -> InvalidButPluginDoesNotCrash:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def __aenter__() -> InvalidButPluginDoesNotCrash:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class BadIterator1(Iterator[int]):
|
||||||
|
def __iter__(self) -> Iterator[int]:
|
||||||
|
... # 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: ..."
|
||||||
|
|
||||||
|
|
||||||
|
class BadIterator2(
|
||||||
|
typing.Iterator[int]
|
||||||
|
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||||
|
def __iter__(self) -> Iterator[int]:
|
||||||
|
... # 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: ..."
|
||||||
|
|
||||||
|
|
||||||
|
class BadIterator3(
|
||||||
|
typing.Iterator[int]
|
||||||
|
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||||
|
def __iter__(self) -> collections.abc.Iterator[int]:
|
||||||
|
... # 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: ..."
|
||||||
|
|
||||||
|
|
||||||
|
class BadIterator4(Iterator[int]):
|
||||||
|
# Note: *Iterable*, not *Iterator*, returned!
|
||||||
|
def __iter__(self) -> Iterable[int]:
|
||||||
|
... # 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: ..."
|
||||||
|
|
||||||
|
|
||||||
|
class IteratorReturningIterable:
|
||||||
|
def __iter__(self) -> Iterable[str]:
|
||||||
|
... # Y045 "__iter__" methods should return an Iterator, not an Iterable
|
||||||
|
|
||||||
|
|
||||||
|
class BadAsyncIterator(collections.abc.AsyncIterator[str]):
|
||||||
|
def __aiter__(self) -> typing.AsyncIterator[str]:
|
||||||
|
... # 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)
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncIteratorReturningAsyncIterable:
|
||||||
|
def __aiter__(self) -> AsyncIterable[str]:
|
||||||
|
... # Y045 "__aiter__" methods should return an AsyncIterator, not an AsyncIterable
|
||||||
|
|
||||||
|
|
||||||
|
class Abstract(Iterator[str]):
|
||||||
|
@abstractmethod
|
||||||
|
def __iter__(self) -> Iterator[str]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __enter__(self) -> Abstract:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def __aenter__(self) -> Abstract:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class GoodIterator(Iterator[str]):
|
||||||
|
def __iter__(self: Self) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class GoodAsyncIterator(AsyncIterator[int]):
|
||||||
|
def __aiter__(self: Self) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class DoesNotInheritFromIterator:
|
||||||
|
def __iter__(self) -> DoesNotInheritFromIterator:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Unannotated:
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __aiter__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __eq__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __ne__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __iadd__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def __ior__(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def __ne__(self, other: Any) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def __imul__(self, other: Any) -> list[str]:
|
||||||
|
...
|
188
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.pyi
vendored
Normal file
188
crates/ruff/resources/test/fixtures/flake8_pyi/PYI034.pyi
vendored
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
# flags: --extend-ignore=Y023
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import builtins
|
||||||
|
import collections.abc
|
||||||
|
import typing
|
||||||
|
from abc import abstractmethod
|
||||||
|
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||||
|
from typing import Any, overload
|
||||||
|
|
||||||
|
import typing_extensions
|
||||||
|
from _typeshed import Self
|
||||||
|
from typing_extensions import final
|
||||||
|
|
||||||
|
class Bad(
|
||||||
|
object
|
||||||
|
): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||||
|
def __new__(
|
||||||
|
cls, *args: Any, **kwargs: Any
|
||||||
|
) -> 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: ..."
|
||||||
|
def __repr__(
|
||||||
|
self,
|
||||||
|
) -> str: ... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||||
|
def __str__(
|
||||||
|
self,
|
||||||
|
) -> builtins.str: ... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant
|
||||||
|
def __eq__(
|
||||||
|
self, other: Any
|
||||||
|
) -> bool: ... # Y032 Prefer "object" to "Any" for the second parameter in "__eq__" methods
|
||||||
|
def __ne__(
|
||||||
|
self, other: typing.Any
|
||||||
|
) -> typing.Any: ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
|
||||||
|
def __enter__(
|
||||||
|
self,
|
||||||
|
) -> 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: ..."
|
||||||
|
async def __aenter__(
|
||||||
|
self,
|
||||||
|
) -> 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: ..."
|
||||||
|
def __iadd__(
|
||||||
|
self, other: Bad
|
||||||
|
) -> 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: ..."
|
||||||
|
|
||||||
|
class AlsoBad(
|
||||||
|
int, builtins.object
|
||||||
|
): ... # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||||
|
|
||||||
|
class Good:
|
||||||
|
def __new__(cls: type[Self], *args: Any, **kwargs: Any) -> Self: ...
|
||||||
|
@abstractmethod
|
||||||
|
def __str__(self) -> str: ...
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __repr__(self) -> str: ...
|
||||||
|
def __eq__(self, other: object) -> bool: ...
|
||||||
|
def __ne__(self, obj: object) -> int: ...
|
||||||
|
def __enter__(self: Self) -> Self: ...
|
||||||
|
async def __aenter__(self: Self) -> Self: ...
|
||||||
|
def __ior__(self: Self, other: Self) -> Self: ...
|
||||||
|
|
||||||
|
class Fine:
|
||||||
|
@overload
|
||||||
|
def __new__(cls, foo: int) -> FineSubclass: ...
|
||||||
|
@overload
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> Fine: ...
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __str__(self) -> str: ...
|
||||||
|
@abc.abstractmethod
|
||||||
|
def __repr__(self) -> str: ...
|
||||||
|
def __eq__(self, other: Any, strange_extra_arg: list[str]) -> Any: ...
|
||||||
|
def __ne__(self, *, kw_only_other: Any) -> bool: ...
|
||||||
|
def __enter__(self) -> None: ...
|
||||||
|
async def __aenter__(self) -> bool: ...
|
||||||
|
|
||||||
|
class FineSubclass(Fine): ...
|
||||||
|
|
||||||
|
class StrangeButAcceptable(str):
|
||||||
|
@typing_extensions.overload
|
||||||
|
def __new__(cls, foo: int) -> StrangeButAcceptableSubclass: ...
|
||||||
|
@typing_extensions.overload
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> StrangeButAcceptable: ...
|
||||||
|
def __str__(self) -> StrangeButAcceptable: ...
|
||||||
|
def __repr__(self) -> StrangeButAcceptable: ...
|
||||||
|
|
||||||
|
class StrangeButAcceptableSubclass(StrangeButAcceptable): ...
|
||||||
|
|
||||||
|
class FineAndDandy:
|
||||||
|
def __str__(self, weird_extra_arg) -> str: ...
|
||||||
|
def __repr__(self, weird_extra_arg_with_default=...) -> str: ...
|
||||||
|
|
||||||
|
@final
|
||||||
|
class WillNotBeSubclassed:
|
||||||
|
def __new__(cls, *args: Any, **kwargs: Any) -> WillNotBeSubclassed: ...
|
||||||
|
def __enter__(self) -> WillNotBeSubclassed: ...
|
||||||
|
async def __aenter__(self) -> WillNotBeSubclassed: ...
|
||||||
|
|
||||||
|
# we don't emit an error for these; out of scope for a linter
|
||||||
|
class InvalidButPluginDoesNotCrash:
|
||||||
|
def __new__() -> InvalidButPluginDoesNotCrash: ...
|
||||||
|
def __enter__() -> InvalidButPluginDoesNotCrash: ...
|
||||||
|
async def __aenter__() -> InvalidButPluginDoesNotCrash: ...
|
||||||
|
|
||||||
|
class BadIterator1(Iterator[int]):
|
||||||
|
def __iter__(
|
||||||
|
self,
|
||||||
|
) -> Iterator[
|
||||||
|
int
|
||||||
|
]: ... # 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: ..."
|
||||||
|
|
||||||
|
class BadIterator2(
|
||||||
|
typing.Iterator[int]
|
||||||
|
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||||
|
def __iter__(
|
||||||
|
self,
|
||||||
|
) -> Iterator[
|
||||||
|
int
|
||||||
|
]: ... # 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: ..."
|
||||||
|
|
||||||
|
class BadIterator3(
|
||||||
|
typing.Iterator[int]
|
||||||
|
): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||||
|
def __iter__(
|
||||||
|
self,
|
||||||
|
) -> collections.abc.Iterator[
|
||||||
|
int
|
||||||
|
]: ... # 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: ..."
|
||||||
|
|
||||||
|
class BadIterator4(Iterator[int]):
|
||||||
|
# Note: *Iterable*, not *Iterator*, returned!
|
||||||
|
def __iter__(
|
||||||
|
self,
|
||||||
|
) -> Iterable[
|
||||||
|
int
|
||||||
|
]: ... # 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: ..."
|
||||||
|
|
||||||
|
class IteratorReturningIterable:
|
||||||
|
def __iter__(
|
||||||
|
self,
|
||||||
|
) -> Iterable[
|
||||||
|
str
|
||||||
|
]: ... # Y045 "__iter__" methods should return an Iterator, not an Iterable
|
||||||
|
|
||||||
|
class BadAsyncIterator(collections.abc.AsyncIterator[str]):
|
||||||
|
def __aiter__(
|
||||||
|
self,
|
||||||
|
) -> typing.AsyncIterator[
|
||||||
|
str
|
||||||
|
]: ... # 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)
|
||||||
|
|
||||||
|
class AsyncIteratorReturningAsyncIterable:
|
||||||
|
def __aiter__(
|
||||||
|
self,
|
||||||
|
) -> AsyncIterable[
|
||||||
|
str
|
||||||
|
]: ... # Y045 "__aiter__" methods should return an AsyncIterator, not an AsyncIterable
|
||||||
|
|
||||||
|
class Abstract(Iterator[str]):
|
||||||
|
@abstractmethod
|
||||||
|
def __iter__(self) -> Iterator[str]: ...
|
||||||
|
@abstractmethod
|
||||||
|
def __enter__(self) -> Abstract: ...
|
||||||
|
@abstractmethod
|
||||||
|
async def __aenter__(self) -> Abstract: ...
|
||||||
|
|
||||||
|
class GoodIterator(Iterator[str]):
|
||||||
|
def __iter__(self: Self) -> Self: ...
|
||||||
|
|
||||||
|
class GoodAsyncIterator(AsyncIterator[int]):
|
||||||
|
def __aiter__(self: Self) -> Self: ...
|
||||||
|
|
||||||
|
class DoesNotInheritFromIterator:
|
||||||
|
def __iter__(self) -> DoesNotInheritFromIterator: ...
|
||||||
|
|
||||||
|
class Unannotated:
|
||||||
|
def __new__(cls, *args, **kwargs): ...
|
||||||
|
def __iter__(self): ...
|
||||||
|
def __aiter__(self): ...
|
||||||
|
async def __aenter__(self): ...
|
||||||
|
def __repr__(self): ...
|
||||||
|
def __str__(self): ...
|
||||||
|
def __eq__(self): ...
|
||||||
|
def __ne__(self): ...
|
||||||
|
def __iadd__(self): ...
|
||||||
|
def __ior__(self): ...
|
||||||
|
|
||||||
|
def __repr__(self) -> str: ...
|
||||||
|
def __str__(self) -> str: ...
|
||||||
|
def __eq__(self, other: Any) -> bool: ...
|
||||||
|
def __ne__(self, other: Any) -> bool: ...
|
||||||
|
def __imul__(self, other: Any) -> list[str]: ...
|
|
@ -436,6 +436,17 @@ where
|
||||||
if self.enabled(Rule::AnyEqNeAnnotation) {
|
if self.enabled(Rule::AnyEqNeAnnotation) {
|
||||||
flake8_pyi::rules::any_eq_ne_annotation(self, name, args);
|
flake8_pyi::rules::any_eq_ne_annotation(self, name, args);
|
||||||
}
|
}
|
||||||
|
if self.enabled(Rule::NonSelfReturnType) {
|
||||||
|
flake8_pyi::rules::non_self_return_type(
|
||||||
|
self,
|
||||||
|
stmt,
|
||||||
|
name,
|
||||||
|
decorator_list,
|
||||||
|
returns.as_ref().map(|expr| &**expr),
|
||||||
|
args,
|
||||||
|
stmt.is_async_function_def_stmt(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.enabled(Rule::DunderFunctionName) {
|
if self.enabled(Rule::DunderFunctionName) {
|
||||||
|
|
|
@ -596,6 +596,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Flake8Pyi, "025") => (RuleGroup::Unspecified, Rule::UnaliasedCollectionsAbcSetImport),
|
(Flake8Pyi, "025") => (RuleGroup::Unspecified, Rule::UnaliasedCollectionsAbcSetImport),
|
||||||
(Flake8Pyi, "032") => (RuleGroup::Unspecified, Rule::AnyEqNeAnnotation),
|
(Flake8Pyi, "032") => (RuleGroup::Unspecified, Rule::AnyEqNeAnnotation),
|
||||||
(Flake8Pyi, "033") => (RuleGroup::Unspecified, Rule::TypeCommentInStub),
|
(Flake8Pyi, "033") => (RuleGroup::Unspecified, Rule::TypeCommentInStub),
|
||||||
|
(Flake8Pyi, "034") => (RuleGroup::Unspecified, Rule::NonSelfReturnType),
|
||||||
(Flake8Pyi, "042") => (RuleGroup::Unspecified, Rule::SnakeCaseTypeAlias),
|
(Flake8Pyi, "042") => (RuleGroup::Unspecified, Rule::SnakeCaseTypeAlias),
|
||||||
(Flake8Pyi, "043") => (RuleGroup::Unspecified, Rule::TSuffixedTypeAlias),
|
(Flake8Pyi, "043") => (RuleGroup::Unspecified, Rule::TSuffixedTypeAlias),
|
||||||
(Flake8Pyi, "045") => (RuleGroup::Unspecified, Rule::IterMethodReturnIterable),
|
(Flake8Pyi, "045") => (RuleGroup::Unspecified, Rule::IterMethodReturnIterable),
|
||||||
|
|
|
@ -519,6 +519,7 @@ ruff_macros::register_rules!(
|
||||||
rules::flake8_pyi::rules::IterMethodReturnIterable,
|
rules::flake8_pyi::rules::IterMethodReturnIterable,
|
||||||
rules::flake8_pyi::rules::DuplicateUnionMember,
|
rules::flake8_pyi::rules::DuplicateUnionMember,
|
||||||
rules::flake8_pyi::rules::EllipsisInNonEmptyClassBody,
|
rules::flake8_pyi::rules::EllipsisInNonEmptyClassBody,
|
||||||
|
rules::flake8_pyi::rules::NonSelfReturnType,
|
||||||
rules::flake8_pyi::rules::CollectionsNamedTuple,
|
rules::flake8_pyi::rules::CollectionsNamedTuple,
|
||||||
rules::flake8_pyi::rules::StringOrBytesTooLong,
|
rules::flake8_pyi::rules::StringOrBytesTooLong,
|
||||||
rules::flake8_pyi::rules::NonEmptyStubBody,
|
rules::flake8_pyi::rules::NonEmptyStubBody,
|
||||||
|
|
|
@ -20,14 +20,16 @@ mod tests {
|
||||||
#[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.pyi"))]
|
#[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.pyi"))]
|
||||||
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))]
|
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))]
|
||||||
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
|
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
|
||||||
|
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.py"))]
|
||||||
|
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.pyi"))]
|
||||||
#[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))]
|
#[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))]
|
||||||
#[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))]
|
#[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))]
|
||||||
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.py"))]
|
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.py"))]
|
||||||
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))]
|
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))]
|
||||||
#[test_case(Rule::EllipsisInNonEmptyClassBody, Path::new("PYI013.py"))]
|
#[test_case(Rule::EllipsisInNonEmptyClassBody, Path::new("PYI013.py"))]
|
||||||
#[test_case(Rule::EllipsisInNonEmptyClassBody, Path::new("PYI013.pyi"))]
|
#[test_case(Rule::EllipsisInNonEmptyClassBody, Path::new("PYI013.pyi"))]
|
||||||
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.py"))]
|
#[test_case(Rule::NonSelfReturnType, Path::new("PYI034.py"))]
|
||||||
#[test_case(Rule::CollectionsNamedTuple, Path::new("PYI024.pyi"))]
|
#[test_case(Rule::NonSelfReturnType, Path::new("PYI034.pyi"))]
|
||||||
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.py"))]
|
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.py"))]
|
||||||
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.pyi"))]
|
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.pyi"))]
|
||||||
#[test_case(Rule::NumericLiteralTooLong, Path::new("PYI054.py"))]
|
#[test_case(Rule::NumericLiteralTooLong, Path::new("PYI054.py"))]
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub(crate) use iter_method_return_iterable::{
|
||||||
iter_method_return_iterable, IterMethodReturnIterable,
|
iter_method_return_iterable, IterMethodReturnIterable,
|
||||||
};
|
};
|
||||||
pub(crate) use non_empty_stub_body::{non_empty_stub_body, NonEmptyStubBody};
|
pub(crate) use non_empty_stub_body::{non_empty_stub_body, NonEmptyStubBody};
|
||||||
|
pub(crate) use non_self_return_type::{non_self_return_type, NonSelfReturnType};
|
||||||
pub(crate) use numeric_literal_too_long::{numeric_literal_too_long, NumericLiteralTooLong};
|
pub(crate) use numeric_literal_too_long::{numeric_literal_too_long, NumericLiteralTooLong};
|
||||||
pub(crate) use pass_in_class_body::{pass_in_class_body, PassInClassBody};
|
pub(crate) use pass_in_class_body::{pass_in_class_body, PassInClassBody};
|
||||||
pub(crate) use pass_statement_stub_body::{pass_statement_stub_body, PassStatementStubBody};
|
pub(crate) use pass_statement_stub_body::{pass_statement_stub_body, PassStatementStubBody};
|
||||||
|
@ -45,6 +46,7 @@ mod duplicate_union_member;
|
||||||
mod ellipsis_in_non_empty_class_body;
|
mod ellipsis_in_non_empty_class_body;
|
||||||
mod iter_method_return_iterable;
|
mod iter_method_return_iterable;
|
||||||
mod non_empty_stub_body;
|
mod non_empty_stub_body;
|
||||||
|
mod non_self_return_type;
|
||||||
mod numeric_literal_too_long;
|
mod numeric_literal_too_long;
|
||||||
mod pass_in_class_body;
|
mod pass_in_class_body;
|
||||||
mod pass_statement_stub_body;
|
mod pass_statement_stub_body;
|
||||||
|
|
298
crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs
Normal file
298
crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
use rustpython_parser::ast::{self, Arguments, Expr, Stmt};
|
||||||
|
|
||||||
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::helpers::{identifier_range, map_subscript};
|
||||||
|
use ruff_python_semantic::analyze::visibility::{is_abstract, is_final, is_overload};
|
||||||
|
use ruff_python_semantic::model::SemanticModel;
|
||||||
|
use ruff_python_semantic::scope::ScopeKind;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for methods that are annotated with a fixed return type, which
|
||||||
|
/// should instead be returning `self`.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// If methods like `__new__` or `__enter__` are annotated with a fixed return
|
||||||
|
/// type, and the class is subclassed, type checkers will not be able to infer
|
||||||
|
/// the correct return type.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
/// ```python
|
||||||
|
/// class Shape:
|
||||||
|
/// def set_scale(self, scale: float) -> Shape:
|
||||||
|
/// self.scale = scale
|
||||||
|
/// return self
|
||||||
|
///
|
||||||
|
/// class Circle(Shape):
|
||||||
|
/// def set_radius(self, radius: float) -> Circle:
|
||||||
|
/// self.radius = radius
|
||||||
|
/// return self
|
||||||
|
///
|
||||||
|
/// # This returns `Shape`, not `Circle`.
|
||||||
|
/// Circle().set_scale(0.5)
|
||||||
|
///
|
||||||
|
/// # Thus, this expression is invalid, as `Shape` has no attribute `set_radius`.
|
||||||
|
/// Circle().set_scale(0.5).set_radius(2.7)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Specifically, this check enforces that the return type of the following
|
||||||
|
/// methods is `Self`:
|
||||||
|
///
|
||||||
|
/// 1. In-place binary operations, like `__iadd__`, `__imul__`, etc.
|
||||||
|
/// 1. `__new__`, `__enter__`, and `__aenter__`, if those methods return the
|
||||||
|
/// class name.
|
||||||
|
/// 1. `__iter__` methods that return `Iterator`, despite the class inheriting
|
||||||
|
/// directly from `Iterator`.
|
||||||
|
/// 1. `__aiter__` methods that return `AsyncIterator`, despite the class
|
||||||
|
/// inheriting directly from `AsyncIterator`.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// class Foo:
|
||||||
|
/// def __new__(cls, *args: Any, **kwargs: Any) -> Bad:
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// def __enter__(self) -> Bad:
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// async def __aenter__(self) -> Bad:
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// def __iadd__(self, other: Bad) -> Bad:
|
||||||
|
/// ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// from typing_extensions import Self
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// class Foo:
|
||||||
|
/// def __new__(cls, *args: Any, **kwargs: Any) -> Self:
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// def __enter__(self) -> Self:
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// async def __aenter__(self) -> Self:
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// def __iadd__(self, other: Bad) -> Self:
|
||||||
|
/// ...
|
||||||
|
/// ```
|
||||||
|
/// ## References
|
||||||
|
/// - [PEP 673](https://peps.python.org/pep-0673/)
|
||||||
|
#[violation]
|
||||||
|
pub struct NonSelfReturnType {
|
||||||
|
class_name: String,
|
||||||
|
method_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Violation for NonSelfReturnType {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
let NonSelfReturnType {
|
||||||
|
class_name,
|
||||||
|
method_name,
|
||||||
|
} = self;
|
||||||
|
if matches!(class_name.as_str(), "__new__") {
|
||||||
|
format!("`__new__` methods usually return `self` at runtime")
|
||||||
|
} else {
|
||||||
|
format!("`{method_name}` methods in classes like `{class_name}` usually return `self` at runtime")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn autofix_title(&self) -> Option<String> {
|
||||||
|
Some("Consider using `typing_extensions.Self` as return type".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PYI034
|
||||||
|
pub(crate) fn non_self_return_type(
|
||||||
|
checker: &mut Checker,
|
||||||
|
stmt: &Stmt,
|
||||||
|
name: &str,
|
||||||
|
decorator_list: &[Expr],
|
||||||
|
returns: Option<&Expr>,
|
||||||
|
args: &Arguments,
|
||||||
|
async_: bool,
|
||||||
|
) {
|
||||||
|
let ScopeKind::Class(class_def) = checker.semantic_model().scope().kind else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.args.is_empty() && args.posonlyargs.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(returns) = returns else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip any abstract or overloaded methods.
|
||||||
|
if is_abstract(checker.semantic_model(), decorator_list)
|
||||||
|
|| is_overload(checker.semantic_model(), decorator_list)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if async_ {
|
||||||
|
if name == "__aenter__"
|
||||||
|
&& is_name(returns, &class_def.name)
|
||||||
|
&& !is_final(checker.semantic_model(), &class_def.decorator_list)
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
NonSelfReturnType {
|
||||||
|
class_name: class_def.name.to_string(),
|
||||||
|
method_name: name.to_string(),
|
||||||
|
},
|
||||||
|
identifier_range(stmt, checker.locator),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In-place methods that are expected to return `Self`.
|
||||||
|
if INPLACE_BINOP_METHODS.contains(&name) {
|
||||||
|
if !is_self(returns, checker.semantic_model()) {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
NonSelfReturnType {
|
||||||
|
class_name: class_def.name.to_string(),
|
||||||
|
method_name: name.to_string(),
|
||||||
|
},
|
||||||
|
identifier_range(stmt, checker.locator),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_name(returns, &class_def.name) {
|
||||||
|
if matches!(name, "__enter__" | "__new__")
|
||||||
|
&& !is_final(checker.semantic_model(), &class_def.decorator_list)
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
NonSelfReturnType {
|
||||||
|
class_name: class_def.name.to_string(),
|
||||||
|
method_name: name.to_string(),
|
||||||
|
},
|
||||||
|
identifier_range(stmt, checker.locator),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match name {
|
||||||
|
"__iter__" => {
|
||||||
|
if is_iterable(returns, checker.semantic_model())
|
||||||
|
&& is_iterator(&class_def.bases, checker.semantic_model())
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
NonSelfReturnType {
|
||||||
|
class_name: class_def.name.to_string(),
|
||||||
|
method_name: name.to_string(),
|
||||||
|
},
|
||||||
|
identifier_range(stmt, checker.locator),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"__aiter__" => {
|
||||||
|
if is_async_iterable(returns, checker.semantic_model())
|
||||||
|
&& is_async_iterator(&class_def.bases, checker.semantic_model())
|
||||||
|
{
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
NonSelfReturnType {
|
||||||
|
class_name: class_def.name.to_string(),
|
||||||
|
method_name: name.to_string(),
|
||||||
|
},
|
||||||
|
identifier_range(stmt, checker.locator),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const INPLACE_BINOP_METHODS: &[&str] = &[
|
||||||
|
"__iadd__",
|
||||||
|
"__isub__",
|
||||||
|
"__imul__",
|
||||||
|
"__imatmul__",
|
||||||
|
"__itruediv__",
|
||||||
|
"__ifloordiv__",
|
||||||
|
"__imod__",
|
||||||
|
"__ipow__",
|
||||||
|
"__ilshift__",
|
||||||
|
"__irshift__",
|
||||||
|
"__iand__",
|
||||||
|
"__ixor__",
|
||||||
|
"__ior__",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
id.as_str() == name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the given expression resolves to `typing.Self`.
|
||||||
|
fn is_self(expr: &Expr, model: &SemanticModel) -> bool {
|
||||||
|
model.match_typing_expr(expr, "Self")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the given class extends `collections.abc.Iterator`.
|
||||||
|
fn is_iterator(bases: &[Expr], model: &SemanticModel) -> bool {
|
||||||
|
bases.iter().any(|expr| {
|
||||||
|
model
|
||||||
|
.resolve_call_path(map_subscript(expr))
|
||||||
|
.map_or(false, |call_path| {
|
||||||
|
matches!(
|
||||||
|
call_path.as_slice(),
|
||||||
|
["typing", "Iterator"] | ["collections", "abc", "Iterator"]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the given expression resolves to `collections.abc.Iterable`.
|
||||||
|
fn is_iterable(expr: &Expr, model: &SemanticModel) -> bool {
|
||||||
|
model
|
||||||
|
.resolve_call_path(map_subscript(expr))
|
||||||
|
.map_or(false, |call_path| {
|
||||||
|
matches!(
|
||||||
|
call_path.as_slice(),
|
||||||
|
["typing", "Iterable" | "Iterator"]
|
||||||
|
| ["collections", "abc", "Iterable" | "Iterator"]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the given class extends `collections.abc.AsyncIterator`.
|
||||||
|
fn is_async_iterator(bases: &[Expr], model: &SemanticModel) -> bool {
|
||||||
|
bases.iter().any(|expr| {
|
||||||
|
model
|
||||||
|
.resolve_call_path(map_subscript(expr))
|
||||||
|
.map_or(false, |call_path| {
|
||||||
|
matches!(
|
||||||
|
call_path.as_slice(),
|
||||||
|
["typing", "AsyncIterator"] | ["collections", "abc", "AsyncIterator"]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the given expression resolves to `collections.abc.AsyncIterable`.
|
||||||
|
fn is_async_iterable(expr: &Expr, model: &SemanticModel) -> bool {
|
||||||
|
model
|
||||||
|
.resolve_call_path(map_subscript(expr))
|
||||||
|
.map_or(false, |call_path| {
|
||||||
|
matches!(
|
||||||
|
call_path.as_slice(),
|
||||||
|
["typing", "AsyncIterable" | "AsyncIterator"]
|
||||||
|
| ["collections", "abc", "AsyncIterable" | "AsyncIterator"]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||||
|
---
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||||
|
---
|
||||||
|
PYI034.pyi:18:9: PYI034 `__new__` methods in classes like `Bad` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
18 | object
|
||||||
|
19 | ): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
|
||||||
|
20 | def __new__(
|
||||||
|
| ^^^^^^^ PYI034
|
||||||
|
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: ..."
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:33:9: PYI034 `__enter__` methods in classes like `Bad` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
33 | self, other: typing.Any
|
||||||
|
34 | ) -> typing.Any: ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
|
||||||
|
35 | def __enter__(
|
||||||
|
| ^^^^^^^^^ PYI034
|
||||||
|
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: ..."
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:36:15: PYI034 `__aenter__` methods in classes like `Bad` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
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: ..."
|
||||||
|
38 | async def __aenter__(
|
||||||
|
| ^^^^^^^^^^ PYI034
|
||||||
|
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: ..."
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:39:9: PYI034 `__iadd__` methods in classes like `Bad` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
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: ..."
|
||||||
|
41 | def __iadd__(
|
||||||
|
| ^^^^^^^^ PYI034
|
||||||
|
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: ..."
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:102:9: PYI034 `__iter__` methods in classes like `BadIterator1` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
102 | class BadIterator1(Iterator[int]):
|
||||||
|
103 | def __iter__(
|
||||||
|
| ^^^^^^^^ PYI034
|
||||||
|
104 | self,
|
||||||
|
105 | ) -> Iterator[
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:111:9: PYI034 `__iter__` methods in classes like `BadIterator2` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
111 | typing.Iterator[int]
|
||||||
|
112 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||||
|
113 | def __iter__(
|
||||||
|
| ^^^^^^^^ PYI034
|
||||||
|
114 | self,
|
||||||
|
115 | ) -> Iterator[
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:120:9: PYI034 `__iter__` methods in classes like `BadIterator3` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
120 | typing.Iterator[int]
|
||||||
|
121 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
|
||||||
|
122 | def __iter__(
|
||||||
|
| ^^^^^^^^ PYI034
|
||||||
|
123 | self,
|
||||||
|
124 | ) -> collections.abc.Iterator[
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:128:9: PYI034 `__iter__` methods in classes like `BadIterator4` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
128 | class BadIterator4(Iterator[int]):
|
||||||
|
129 | # Note: *Iterable*, not *Iterator*, returned!
|
||||||
|
130 | def __iter__(
|
||||||
|
| ^^^^^^^^ PYI034
|
||||||
|
131 | self,
|
||||||
|
132 | ) -> Iterable[
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
PYI034.pyi:142:9: PYI034 `__aiter__` methods in classes like `BadAsyncIterator` usually return `self` at runtime
|
||||||
|
|
|
||||||
|
142 | class BadAsyncIterator(collections.abc.AsyncIterator[str]):
|
||||||
|
143 | def __aiter__(
|
||||||
|
| ^^^^^^^^^ PYI034
|
||||||
|
144 | self,
|
||||||
|
145 | ) -> typing.AsyncIterator[
|
||||||
|
|
|
||||||
|
= help: Consider using `typing_extensions.Self` as return type
|
||||||
|
|
||||||
|
|
|
@ -684,12 +684,27 @@ pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
|
||||||
/// callable.
|
/// callable.
|
||||||
pub fn map_callable(decorator: &Expr) -> &Expr {
|
pub fn map_callable(decorator: &Expr) -> &Expr {
|
||||||
if let Expr::Call(ast::ExprCall { func, .. }) = decorator {
|
if let Expr::Call(ast::ExprCall { func, .. }) = decorator {
|
||||||
|
// Ex) `@decorator()`
|
||||||
func
|
func
|
||||||
} else {
|
} else {
|
||||||
|
// Ex) `@decorator`
|
||||||
decorator
|
decorator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given an [`Expr`] that can be callable or not (like a decorator, which could
|
||||||
|
/// be used with or without explicit call syntax), return the underlying
|
||||||
|
/// callable.
|
||||||
|
pub fn map_subscript(expr: &Expr) -> &Expr {
|
||||||
|
if let Expr::Subscript(ast::ExprSubscript { value, .. }) = expr {
|
||||||
|
// Ex) `Iterable[T]`
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
// Ex) `Iterable`
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if a statement or expression includes at least one comment.
|
/// Returns `true` if a statement or expression includes at least one comment.
|
||||||
pub fn has_comments<T>(located: &T, locator: &Locator) -> bool
|
pub fn has_comments<T>(located: &T, locator: &Locator) -> bool
|
||||||
where
|
where
|
||||||
|
|
|
@ -89,6 +89,14 @@ pub fn is_property(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if a class is an `final`.
|
||||||
|
pub fn is_final(model: &SemanticModel, decorator_list: &[Expr]) -> bool {
|
||||||
|
decorator_list
|
||||||
|
.iter()
|
||||||
|
.any(|expr| model.match_typing_expr(map_callable(expr), "final"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if a function is a "magic method".
|
/// Returns `true` if a function is a "magic method".
|
||||||
pub fn is_magic(name: &str) -> bool {
|
pub fn is_magic(name: &str) -> bool {
|
||||||
name.starts_with("__") && name.ends_with("__")
|
name.starts_with("__") && name.ends_with("__")
|
||||||
|
|
|
@ -93,6 +93,10 @@ impl<'a> SemanticModel<'a> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if call_path.as_slice() == ["_typeshed", target] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if TYPING_EXTENSIONS.contains(target) {
|
if TYPING_EXTENSIONS.contains(target) {
|
||||||
if call_path.as_slice() == ["typing_extensions", target] {
|
if call_path.as_slice() == ["typing_extensions", target] {
|
||||||
return true;
|
return true;
|
||||||
|
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -2244,6 +2244,7 @@
|
||||||
"PYI03",
|
"PYI03",
|
||||||
"PYI032",
|
"PYI032",
|
||||||
"PYI033",
|
"PYI033",
|
||||||
|
"PYI034",
|
||||||
"PYI04",
|
"PYI04",
|
||||||
"PYI042",
|
"PYI042",
|
||||||
"PYI043",
|
"PYI043",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue