Skip to content

Commit 7f640ab

Browse files
authored
[builtins] Fix annotations for reversed.__new__ (#11646)
Previously, `reversed(...)` always returned a `reversed` instance, although in reality, the return type of a `__reversed__()` dunder is preferred.
1 parent d542397 commit 7f640ab

2 files changed

Lines changed: 32 additions & 6 deletions

File tree

stdlib/@tests/test_cases/builtins/check_reversed.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,18 @@ def __getitem__(self, item: int) -> _T:
3131

3232

3333
len_and_get_item: MyLenAndGetItem[int] = MyLenAndGetItem()
34-
assert_type(list(reversed(len_and_get_item)), "list[int]")
34+
assert_type(reversed(len_and_get_item), "reversed[int]")
35+
36+
37+
class UnTrue:
38+
def __reversed__(self) -> UnFalse:
39+
return UnFalse()
40+
41+
42+
class UnFalse:
43+
def __reversed__(self) -> UnTrue:
44+
return UnTrue()
45+
46+
47+
assert_type(reversed(UnTrue()), "UnFalse")
48+
assert_type(reversed(reversed(UnTrue())), "UnTrue")

stdlib/builtins.pyi

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ from _typeshed import (
3030
SupportsRichComparisonT,
3131
SupportsWrite,
3232
)
33-
from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized
33+
from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Set as AbstractSet, Sized
3434
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
3535
from os import PathLike
3636
from types import CellType, CodeType, EllipsisType, GenericAlias, NotImplementedType, TracebackType, UnionType
@@ -281,6 +281,7 @@ class int:
281281
*,
282282
signed: bool = False,
283283
) -> Self: ...
284+
284285
else:
285286
def to_bytes(self, length: SupportsIndex, byteorder: Literal["little", "big"], *, signed: bool = False) -> bytes: ...
286287
@classmethod
@@ -344,6 +345,7 @@ class int:
344345
def __floor__(self) -> int: ...
345346
if sys.version_info >= (3, 14):
346347
def __round__(self, ndigits: SupportsIndex | None = None, /) -> int: ...
348+
347349
else:
348350
def __round__(self, ndigits: SupportsIndex = ..., /) -> int: ...
349351

@@ -770,6 +772,7 @@ class bytes(Sequence[int]):
770772
def partition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ...
771773
if sys.version_info >= (3, 15):
772774
def replace(self, old: ReadableBuffer, new: ReadableBuffer, /, count: SupportsIndex = -1) -> bytes: ...
775+
773776
else:
774777
def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ...
775778

@@ -890,6 +893,7 @@ class bytearray(MutableSequence[int]):
890893
def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ...
891894
if sys.version_info >= (3, 15):
892895
def replace(self, old: ReadableBuffer, new: ReadableBuffer, /, count: SupportsIndex = -1) -> bytearray: ...
896+
893897
else:
894898
def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ...
895899

@@ -1042,6 +1046,7 @@ class memoryview(Sequence[_I]):
10421046
if sys.version_info >= (3, 14):
10431047
def index(self, value: object, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ...
10441048
def count(self, value: object, /) -> int: ...
1049+
10451050
else:
10461051
# These are inherited from the Sequence ABC, but don't actually exist on memoryview.
10471052
# See https://github.com/python/cpython/issues/125420
@@ -1128,6 +1133,7 @@ class slice(Generic[_StartT_co, _StopT_co, _StepT_co]):
11281133
def __eq__(self, value: object, /) -> bool: ...
11291134
if sys.version_info >= (3, 12):
11301135
def __hash__(self) -> int: ...
1136+
11311137
else:
11321138
__hash__: ClassVar[None] # type: ignore[assignment]
11331139

@@ -1202,6 +1208,7 @@ class function:
12021208
closure: tuple[CellType, ...] | None = None,
12031209
kwdefaults: dict[str, object] | None = None,
12041210
) -> Self: ...
1211+
12051212
else:
12061213
def __new__(
12071214
cls,
@@ -1359,6 +1366,7 @@ class dict(MutableMapping[_KT, _VT]):
13591366
def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
13601367
@overload
13611368
def __ror__(self, value: frozendict[_T1, _T2], /) -> frozendict[_KT | _T1, _VT | _T2]: ...
1369+
13621370
else:
13631371
def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
13641372
def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
@@ -2145,15 +2153,19 @@ def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = None) -> complex
21452153

21462154
quit: _sitebuiltins.Quitter
21472155

2156+
@type_check_only
2157+
class _SupportsReversed(Protocol[_T_co]):
2158+
def __reversed__(self) -> _T_co: ...
2159+
21482160
@disjoint_base
2149-
class reversed(Generic[_T]):
2161+
class reversed(Generic[_T_co]):
21502162
@overload
2151-
def __new__(cls, sequence: Reversible[_T], /) -> Iterator[_T]: ... # type: ignore[misc]
2163+
def __new__(cls, sequence: _SupportsReversed[_T], /) -> _T: ... # type: ignore[misc]
21522164
@overload
2153-
def __new__(cls, sequence: SupportsLenAndGetItem[_T], /) -> Iterator[_T]: ... # type: ignore[misc]
2165+
def __new__(cls, sequence: SupportsLenAndGetItem[_T_co], /) -> Self: ...
21542166

21552167
def __iter__(self) -> Self: ...
2156-
def __next__(self) -> _T: ...
2168+
def __next__(self) -> _T_co: ...
21572169
def __length_hint__(self) -> int: ...
21582170

21592171
def repr(obj: object, /) -> str: ...

0 commit comments

Comments
 (0)