From c00a660e6c055e20a604efe4445e8aeea8eb88da Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 3 Dec 2021 17:18:00 +0000 Subject: [PATCH 1/3] Add `OrderedDict.fromkeys` Closes #3800 (no other solution possible until we have higher-kinded `TypeVar`s) --- stdlib/collections/__init__.pyi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 5f8200d5c25f..895680e162d6 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -273,6 +273,12 @@ class OrderedDict(Dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): def keys(self) -> _OrderedDictKeysView[_KT, _VT]: ... def items(self) -> _OrderedDictItemsView[_KT, _VT]: ... def values(self) -> _OrderedDictValuesView[_KT, _VT]: ... + @classmethod + @overload + def fromkeys(cls, __iterable: Iterable[_T], __value: None = ...) -> OrderedDict[_T, Any | None]: ... + @classmethod + @overload + def fromkeys(cls, __iterable: Iterable[_T], __value: _S) -> OrderedDict[_T, _S]: ... class defaultdict(Dict[_KT, _VT], Generic[_KT, _VT]): default_factory: Callable[[], _VT] | None From f4ac71c731a17d4284faff5b970a56fe640c5f93 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 3 Dec 2021 19:11:36 +0000 Subject: [PATCH 2/3] Add clarifying comments --- stdlib/builtins.pyi | 5 +++++ stdlib/collections/__init__.pyi | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index bb18586ca0ef..298192c7a101 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -777,11 +777,13 @@ class list(MutableSequence[_T], Generic[_T]): def append(self, __object: _T) -> None: ... def extend(self, __iterable: Iterable[_T]) -> None: ... def pop(self, __index: SupportsIndex = ...) -> _T: ... + # Signature of `list.index` should be kept in line with `collections.UserList.index()` def index(self, __value: _T, __start: SupportsIndex = ..., __stop: SupportsIndex = ...) -> int: ... def count(self, __value: _T) -> int: ... def insert(self, __index: SupportsIndex, __object: _T) -> None: ... def remove(self, __value: _T) -> None: ... def reverse(self) -> None: ... + # Signature of `list.sort` should be kept inline with `collections.UserList.sort()` @overload def sort(self: list[SupportsLessThanT], *, key: None = ..., reverse: bool = ...) -> None: ... @overload @@ -840,6 +842,9 @@ class dict(MutableMapping[_KT, _VT], Generic[_KT, _VT]): def keys(self) -> dict_keys[_KT, _VT]: ... def values(self) -> dict_values[_KT, _VT]: ... def items(self) -> dict_items[_KT, _VT]: ... + # Signature of `dict.fromkeys` should be kept identical to `fromkeys` methods in `collections.OrderedDict`/`collections.ChainMap` + # TODO: the true signature of `dict.fromkeys` is not expressable in the current type system. + # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @overload def fromkeys(cls, __iterable: Iterable[_T], __value: None = ...) -> dict[_T, Any | None]: ... diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 895680e162d6..17094bfb3f49 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -77,8 +77,10 @@ class UserList(MutableSequence[_T]): def clear(self) -> None: ... def copy(self: _S) -> _S: ... def count(self, item: _T) -> int: ... + # All arguments are passed to `list.index` at runtime, so the signature should be kept in line with `list.index`. def index(self, item: _T, __start: SupportsIndex = ..., __stop: SupportsIndex = ...) -> int: ... def reverse(self) -> None: ... + # All arguments are passed to `list.sort` at runtime, so the signature should be kept in line with `list.sort`. @overload def sort(self: UserList[SupportsLessThanT], *, key: None = ..., reverse: bool = ...) -> None: ... @overload @@ -273,6 +275,9 @@ class OrderedDict(Dict[_KT, _VT], Reversible[_KT], Generic[_KT, _VT]): def keys(self) -> _OrderedDictKeysView[_KT, _VT]: ... def items(self) -> _OrderedDictItemsView[_KT, _VT]: ... def values(self) -> _OrderedDictValuesView[_KT, _VT]: ... + # `fromkeys` is actually inherited from `dict` at runtime, so the signature should be kept in line with `dict.fromkeys`. + # Ideally we would not redefine it here, but the true signature of `dict.fromkeys` is not expressable in the current type system. + # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @overload def fromkeys(cls, __iterable: Iterable[_T], __value: None = ...) -> OrderedDict[_T, Any | None]: ... @@ -320,6 +325,7 @@ class ChainMap(MutableMapping[_KT, _VT], Generic[_KT, _VT]): @overload def pop(self, key: _KT, default: _VT | _T = ...) -> _VT | _T: ... def copy(self: Self) -> Self: ... + # All arguments to `fromkeys` are passed to `dict.fromkeys` at runtime, so the signature should be kept in line with `dict.fromkeys`. @classmethod @overload def fromkeys(cls, iterable: Iterable[_T], __value: None = ...) -> ChainMap[_T, Any | None]: ... From 2794d7fcaa2470c2e9b21199795e105eee15fe42 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 4 Dec 2021 17:59:35 +0000 Subject: [PATCH 3/3] Add `__(r)or__` to various `typing` classes - `__or__` was added to `TypeVar` in Python 3.10: https://bugs.python.org/issue41428 (this PR: https://github.com/python/cpython/pull/21515) - `__or__` was added to `ForwardRef` in Python 3.11: https://bugs.python.org/issue45489 --- stdlib/typing.pyi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index a56ea8c8c4d1..195645af4dbd 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -29,11 +29,17 @@ class TypeVar: covariant: bool = ..., contravariant: bool = ..., ) -> None: ... + if sys.version_info >= (3, 10): + def __or__(self, other: Any) -> _SpecialForm: ... + def __ror__(self, other: Any) -> _SpecialForm: ... _promote = object() class _SpecialForm: def __getitem__(self, typeargs: Any) -> object: ... + if sys.version_info >= (3, 10): + def __or__(self, other: Any) -> _SpecialForm: ... + def __ror__(self, other: Any) -> _SpecialForm: ... _F = TypeVar("_F", bound=Callable[..., Any]) _P = _ParamSpec("_P") @@ -80,6 +86,8 @@ if sys.version_info >= (3, 10): def args(self) -> ParamSpecArgs: ... @property def kwargs(self) -> ParamSpecKwargs: ... + def __or__(self, other: Any) -> _SpecialForm: ... + def __ror__(self, other: Any) -> _SpecialForm: ... Concatenate: _SpecialForm = ... TypeAlias: _SpecialForm = ... TypeGuard: _SpecialForm = ... @@ -727,6 +735,9 @@ if sys.version_info >= (3, 7): def __eq__(self, other: Any) -> bool: ... def __hash__(self) -> int: ... def __repr__(self) -> str: ... + if sys.version_info >= (3, 11): + def __or__(self, other: Any) -> _SpecialForm: ... + def __ror__(self, other: Any) -> _SpecialForm: ... if sys.version_info >= (3, 10): def is_typeddict(tp: Any) -> bool: ...