Skip to content

Commit 5513d3f

Browse files
authored
Drop Python 3.8 support in builtins (#13762)
* remove py38 branches in `builtins` * combined `builtins.dict` tests with those exclusive to `>=3.9`
1 parent f6503fd commit 5513d3f

File tree

3 files changed

+95
-122
lines changed

3 files changed

+95
-122
lines changed

stdlib/@tests/test_cases/builtins/check_dict-py39.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

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

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from __future__ import annotations
22

3-
from typing import Any, Dict, Generic, Iterable, TypeVar, Union
4-
from typing_extensions import assert_type
3+
import os
4+
from typing import Any, Dict, Generic, Iterable, Mapping, TypeVar, Union
5+
from typing_extensions import Self, assert_type
56

67
# These do follow `__init__` overloads order:
78
# mypy and pyright have different opinions about this one:
@@ -148,3 +149,61 @@ def test11() -> str:
148149

149150
def test12() -> str:
150151
return d_str.get("key", int_value) # type: ignore[arg-type]
152+
153+
154+
# Tests for `dict.__(r)or__`.
155+
156+
157+
class CustomDictSubclass(dict[_KT, _VT]):
158+
pass
159+
160+
161+
class CustomMappingWithDunderOr(Mapping[_KT, _VT]):
162+
def __or__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]:
163+
return {}
164+
165+
def __ror__(self, other: Mapping[_KT, _VT]) -> dict[_KT, _VT]:
166+
return {}
167+
168+
def __ior__(self, other: Mapping[_KT, _VT]) -> Self:
169+
return self
170+
171+
172+
def test_dict_dot_or(
173+
a: dict[int, int],
174+
b: CustomDictSubclass[int, int],
175+
c: dict[str, str],
176+
d: Mapping[int, int],
177+
e: CustomMappingWithDunderOr[str, str],
178+
) -> None:
179+
# dict.__(r)or__ always returns a dict, even if called on a subclass of dict:
180+
assert_type(a | b, dict[int, int])
181+
assert_type(b | a, dict[int, int])
182+
183+
assert_type(a | c, dict[Union[int, str], Union[int, str]])
184+
185+
# arbitrary mappings are not accepted by `dict.__or__`;
186+
# it has to be a subclass of `dict`
187+
a | d # type: ignore
188+
189+
# but Mappings such as `os._Environ` or `CustomMappingWithDunderOr`,
190+
# which define `__ror__` methods that accept `dict`, are fine:
191+
assert_type(a | os.environ, dict[Union[str, int], Union[str, int]])
192+
assert_type(os.environ | a, dict[Union[str, int], Union[str, int]])
193+
194+
assert_type(c | os.environ, dict[str, str])
195+
assert_type(c | e, dict[str, str])
196+
197+
assert_type(os.environ | c, dict[str, str])
198+
assert_type(e | c, dict[str, str])
199+
200+
# store "untainted" `CustomMappingWithDunderOr[str, str]` to test `__ior__` against ` dict[str, str]` later
201+
# Invalid `e |= a` causes pyright to join `Unknown` to `e`'s type
202+
f = e
203+
204+
e |= c
205+
e |= a # type: ignore
206+
207+
c |= f
208+
209+
c |= a # type: ignore

stdlib/builtins.pyi

Lines changed: 34 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ from _typeshed import (
3232
)
3333
from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized
3434
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
35-
from types import CellType, CodeType, TracebackType
35+
from types import CellType, CodeType, GenericAlias, TracebackType
3636

3737
# mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping}
3838
# are imported from collections.abc in builtins.pyi
@@ -72,9 +72,6 @@ from typing_extensions import ( # noqa: Y023
7272
deprecated,
7373
)
7474

75-
if sys.version_info >= (3, 9):
76-
from types import GenericAlias
77-
7875
_T = TypeVar("_T")
7976
_I = TypeVar("_I", default=int)
8077
_T_co = TypeVar("_T_co", covariant=True)
@@ -377,10 +374,8 @@ class float:
377374
def __rpow__(self, value: float, mod: None = None, /) -> Any: ...
378375
def __getnewargs__(self) -> tuple[float]: ...
379376
def __trunc__(self) -> int: ...
380-
if sys.version_info >= (3, 9):
381-
def __ceil__(self) -> int: ...
382-
def __floor__(self) -> int: ...
383-
377+
def __ceil__(self) -> int: ...
378+
def __floor__(self) -> int: ...
384379
@overload
385380
def __round__(self, ndigits: None = None, /) -> int: ...
386381
@overload
@@ -519,16 +514,15 @@ class str(Sequence[str]):
519514
) -> LiteralString: ...
520515
@overload
521516
def replace(self, old: str, new: str, count: SupportsIndex = -1, /) -> str: ... # type: ignore[misc]
522-
if sys.version_info >= (3, 9):
523-
@overload
524-
def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ...
525-
@overload
526-
def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc]
527-
@overload
528-
def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ...
529-
@overload
530-
def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc]
531517

518+
@overload
519+
def removeprefix(self: LiteralString, prefix: LiteralString, /) -> LiteralString: ...
520+
@overload
521+
def removeprefix(self, prefix: str, /) -> str: ... # type: ignore[misc]
522+
@overload
523+
def removesuffix(self: LiteralString, suffix: LiteralString, /) -> LiteralString: ...
524+
@overload
525+
def removesuffix(self, suffix: str, /) -> str: ... # type: ignore[misc]
532526
def rfind(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
533527
def rindex(self, sub: str, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /) -> int: ...
534528
@overload
@@ -666,10 +660,8 @@ class bytes(Sequence[int]):
666660
def lstrip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ...
667661
def partition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ...
668662
def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ...
669-
if sys.version_info >= (3, 9):
670-
def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ...
671-
def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ...
672-
663+
def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ...
664+
def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ...
673665
def rfind(
674666
self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /
675667
) -> int: ...
@@ -771,10 +763,8 @@ class bytearray(MutableSequence[int]):
771763
def partition(self, sep: ReadableBuffer, /) -> tuple[bytearray, bytearray, bytearray]: ...
772764
def pop(self, index: int = -1, /) -> int: ...
773765
def remove(self, value: int, /) -> None: ...
774-
if sys.version_info >= (3, 9):
775-
def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ...
776-
def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ...
777-
766+
def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ...
767+
def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ...
778768
def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ...
779769
def rfind(
780770
self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = ..., end: SupportsIndex | None = ..., /
@@ -1009,8 +999,7 @@ class tuple(Sequence[_T_co]):
1009999
def __rmul__(self, value: SupportsIndex, /) -> tuple[_T_co, ...]: ...
10101000
def count(self, value: Any, /) -> int: ...
10111001
def index(self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = sys.maxsize, /) -> int: ...
1012-
if sys.version_info >= (3, 9):
1013-
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1002+
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
10141003

10151004
# Doesn't exist at runtime, but deleting this breaks mypy and pyright. See:
10161005
# https://github.com/python/typeshed/issues/7580
@@ -1092,8 +1081,7 @@ class list(MutableSequence[_T]):
10921081
def __lt__(self, value: list[_T], /) -> bool: ...
10931082
def __le__(self, value: list[_T], /) -> bool: ...
10941083
def __eq__(self, value: object, /) -> bool: ...
1095-
if sys.version_info >= (3, 9):
1096-
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1084+
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
10971085

10981086
class dict(MutableMapping[_KT, _VT]):
10991087
# __init__ should be kept roughly in line with `collections.UserDict.__init__`, which has similar semantics
@@ -1162,21 +1150,20 @@ class dict(MutableMapping[_KT, _VT]):
11621150
def __eq__(self, value: object, /) -> bool: ...
11631151
def __reversed__(self) -> Iterator[_KT]: ...
11641152
__hash__: ClassVar[None] # type: ignore[assignment]
1165-
if sys.version_info >= (3, 9):
1166-
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1167-
@overload
1168-
def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
1169-
@overload
1170-
def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
1171-
@overload
1172-
def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
1173-
@overload
1174-
def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
1175-
# dict.__ior__ should be kept roughly in line with MutableMapping.update()
1176-
@overload # type: ignore[misc]
1177-
def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ...
1178-
@overload
1179-
def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ...
1153+
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1154+
@overload
1155+
def __or__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
1156+
@overload
1157+
def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
1158+
@overload
1159+
def __ror__(self, value: dict[_KT, _VT], /) -> dict[_KT, _VT]: ...
1160+
@overload
1161+
def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ...
1162+
# dict.__ior__ should be kept roughly in line with MutableMapping.update()
1163+
@overload # type: ignore[misc]
1164+
def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ...
1165+
@overload
1166+
def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ...
11801167

11811168
class set(MutableSet[_T]):
11821169
@overload
@@ -1215,8 +1202,7 @@ class set(MutableSet[_T]):
12151202
def __gt__(self, value: AbstractSet[object], /) -> bool: ...
12161203
def __eq__(self, value: object, /) -> bool: ...
12171204
__hash__: ClassVar[None] # type: ignore[assignment]
1218-
if sys.version_info >= (3, 9):
1219-
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1205+
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
12201206

12211207
class frozenset(AbstractSet[_T_co]):
12221208
@overload
@@ -1244,15 +1230,13 @@ class frozenset(AbstractSet[_T_co]):
12441230
def __gt__(self, value: AbstractSet[object], /) -> bool: ...
12451231
def __eq__(self, value: object, /) -> bool: ...
12461232
def __hash__(self) -> int: ...
1247-
if sys.version_info >= (3, 9):
1248-
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1233+
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
12491234

12501235
class enumerate(Generic[_T]):
12511236
def __new__(cls, iterable: Iterable[_T], start: int = 0) -> Self: ...
12521237
def __iter__(self) -> Self: ...
12531238
def __next__(self) -> tuple[int, _T]: ...
1254-
if sys.version_info >= (3, 9):
1255-
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
1239+
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
12561240

12571241
@final
12581242
class range(Sequence[int]):

0 commit comments

Comments
 (0)