From 50bc9104a5128d17282e8c70c9af5a3e5b594c5f Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Mon, 19 Jan 2026 22:25:57 +0100 Subject: [PATCH 1/2] Made parameters of collections.abc members positional only - Sequence - MutableSequence - AbstractSet - MutableSet - KeysView - ValuesView - ItemsView --- stdlib/@tests/stubtest_allowlists/common.txt | 19 +++- stdlib/typing.pyi | 108 +++++++++---------- 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/common.txt b/stdlib/@tests/stubtest_allowlists/common.txt index d8956df6b327..2a186aa3f8c7 100644 --- a/stdlib/@tests/stubtest_allowlists/common.txt +++ b/stdlib/@tests/stubtest_allowlists/common.txt @@ -192,8 +192,23 @@ _collections_abc\.ByteString typing\.ByteString _collections_abc.Callable # Typing-related weirdness -_collections_abc.Mapping.get # Adding None to the Union messed up mypy -_collections_abc.Sequence.index # Supporting None in end is not mandatory + +# lots of methods are positional-or-keyword in implementation +# but this is unsafe as canonical types list/dist/set etc. only support positional args +# See: https://github.com/python/typeshed/issues/14071 +# See: https://github.com/python/cpython/issues/135312 +_collections_abc.Mapping.get +_collections_abc.MutableSequence.append +_collections_abc.MutableSequence.extend +_collections_abc.MutableSequence.insert +_collections_abc.MutableSequence.pop +_collections_abc.MutableSequence.remove +_collections_abc.MutableSet.add +_collections_abc.MutableSet.discard +_collections_abc.MutableSet.remove +_collections_abc.Sequence.count +_collections_abc.Sequence.index +_collections_abc.Set.isdisjoint _ctypes.CFuncPtr # stubtest erroneously thinks it can't be subclassed diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 5521055bdef3..efaf3bf5b65c 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -655,76 +655,76 @@ class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): class Sequence(Reversible[_T_co], Collection[_T_co]): @overload @abstractmethod - def __getitem__(self, index: int) -> _T_co: ... + def __getitem__(self, index: int, /) -> _T_co: ... @overload @abstractmethod - def __getitem__(self, index: slice) -> Sequence[_T_co]: ... + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... # Mixin methods - def index(self, value: Any, start: int = 0, stop: int = ...) -> int: ... - def count(self, value: Any) -> int: ... - def __contains__(self, value: object) -> bool: ... + def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... + def count(self, value: Any, /) -> int: ... + def __contains__(self, value: object, /) -> bool: ... def __iter__(self) -> Iterator[_T_co]: ... def __reversed__(self) -> Iterator[_T_co]: ... class MutableSequence(Sequence[_T]): @abstractmethod - def insert(self, index: int, value: _T) -> None: ... + def insert(self, index: int, value: _T, /) -> None: ... @overload @abstractmethod - def __getitem__(self, index: int) -> _T: ... + def __getitem__(self, index: int, /) -> _T: ... @overload @abstractmethod - def __getitem__(self, index: slice) -> MutableSequence[_T]: ... + def __getitem__(self, index: slice, /) -> MutableSequence[_T]: ... @overload @abstractmethod - def __setitem__(self, index: int, value: _T) -> None: ... + def __setitem__(self, index: int, value: _T, /) -> None: ... @overload @abstractmethod - def __setitem__(self, index: slice, value: Iterable[_T]) -> None: ... + def __setitem__(self, index: slice, value: Iterable[_T], /) -> None: ... @overload @abstractmethod - def __delitem__(self, index: int) -> None: ... + def __delitem__(self, index: int, /) -> None: ... @overload @abstractmethod - def __delitem__(self, index: slice) -> None: ... + def __delitem__(self, index: slice, /) -> None: ... # Mixin methods - def append(self, value: _T) -> None: ... + def append(self, value: _T, /) -> None: ... def clear(self) -> None: ... - def extend(self, values: Iterable[_T]) -> None: ... + def extend(self, values: Iterable[_T], /) -> None: ... def reverse(self) -> None: ... - def pop(self, index: int = -1) -> _T: ... - def remove(self, value: _T) -> None: ... - def __iadd__(self, values: Iterable[_T]) -> typing_extensions.Self: ... + def pop(self, index: int = -1, /) -> _T: ... + def remove(self, value: _T, /) -> None: ... + def __iadd__(self, values: Iterable[_T], /) -> typing_extensions.Self: ... class AbstractSet(Collection[_T_co]): @abstractmethod - def __contains__(self, x: object) -> bool: ... + def __contains__(self, x: object, /) -> bool: ... def _hash(self) -> int: ... # Mixin methods - def __le__(self, other: AbstractSet[Any]) -> bool: ... - def __lt__(self, other: AbstractSet[Any]) -> bool: ... - def __gt__(self, other: AbstractSet[Any]) -> bool: ... - def __ge__(self, other: AbstractSet[Any]) -> bool: ... - def __and__(self, other: AbstractSet[Any]) -> AbstractSet[_T_co]: ... - def __or__(self, other: AbstractSet[_T]) -> AbstractSet[_T_co | _T]: ... - def __sub__(self, other: AbstractSet[Any]) -> AbstractSet[_T_co]: ... - def __xor__(self, other: AbstractSet[_T]) -> AbstractSet[_T_co | _T]: ... - def __eq__(self, other: object) -> bool: ... - def isdisjoint(self, other: Iterable[Any]) -> bool: ... + def __le__(self, other: AbstractSet[Any], /) -> bool: ... + def __lt__(self, other: AbstractSet[Any], /) -> bool: ... + def __gt__(self, other: AbstractSet[Any], /) -> bool: ... + def __ge__(self, other: AbstractSet[Any], /) -> bool: ... + def __and__(self, other: AbstractSet[Any], /) -> AbstractSet[_T_co]: ... + def __or__(self, other: AbstractSet[_T], /) -> AbstractSet[_T_co | _T]: ... + def __sub__(self, other: AbstractSet[Any], /) -> AbstractSet[_T_co]: ... + def __xor__(self, other: AbstractSet[_T], /) -> AbstractSet[_T_co | _T]: ... + def __eq__(self, other: object, /) -> bool: ... + def isdisjoint(self, other: Iterable[Any], /) -> bool: ... class MutableSet(AbstractSet[_T]): @abstractmethod - def add(self, value: _T) -> None: ... + def add(self, value: _T, /) -> None: ... @abstractmethod - def discard(self, value: _T) -> None: ... + def discard(self, value: _T, /) -> None: ... # Mixin methods def clear(self) -> None: ... def pop(self) -> _T: ... - def remove(self, value: _T) -> None: ... - def __ior__(self, it: AbstractSet[_T]) -> typing_extensions.Self: ... # type: ignore[override,misc] - def __iand__(self, it: AbstractSet[Any]) -> typing_extensions.Self: ... - def __ixor__(self, it: AbstractSet[_T]) -> typing_extensions.Self: ... # type: ignore[override,misc] - def __isub__(self, it: AbstractSet[Any]) -> typing_extensions.Self: ... + def remove(self, value: _T, /) -> None: ... + def __ior__(self, it: AbstractSet[_T], /) -> typing_extensions.Self: ... # type: ignore[override,misc] + def __iand__(self, it: AbstractSet[Any], /) -> typing_extensions.Self: ... + def __ixor__(self, it: AbstractSet[_T], /) -> typing_extensions.Self: ... # type: ignore[override,misc] + def __isub__(self, it: AbstractSet[Any], /) -> typing_extensions.Self: ... class MappingView(Sized): __slots__ = ("_mapping",) @@ -733,33 +733,33 @@ class MappingView(Sized): class ItemsView(MappingView, AbstractSet[tuple[_KT_co, _VT_co]], Generic[_KT_co, _VT_co]): def __init__(self, mapping: SupportsGetItemViewable[_KT_co, _VT_co]) -> None: ... # undocumented - def __and__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ... - def __rand__(self, other: Iterable[_T]) -> set[_T]: ... - def __contains__(self, item: tuple[object, object]) -> bool: ... # type: ignore[override] + def __and__(self, other: Iterable[Any], /) -> set[tuple[_KT_co, _VT_co]]: ... + def __rand__(self, other: Iterable[_T], /) -> set[_T]: ... + def __contains__(self, item: tuple[object, object], /) -> bool: ... # type: ignore[override] def __iter__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... - def __or__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... - def __ror__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... - def __sub__(self, other: Iterable[Any]) -> set[tuple[_KT_co, _VT_co]]: ... - def __rsub__(self, other: Iterable[_T]) -> set[_T]: ... - def __xor__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... - def __rxor__(self, other: Iterable[_T]) -> set[tuple[_KT_co, _VT_co] | _T]: ... + def __or__(self, other: Iterable[_T], /) -> set[tuple[_KT_co, _VT_co] | _T]: ... + def __ror__(self, other: Iterable[_T], /) -> set[tuple[_KT_co, _VT_co] | _T]: ... + def __sub__(self, other: Iterable[Any], /) -> set[tuple[_KT_co, _VT_co]]: ... + def __rsub__(self, other: Iterable[_T], /) -> set[_T]: ... + def __xor__(self, other: Iterable[_T], /) -> set[tuple[_KT_co, _VT_co] | _T]: ... + def __rxor__(self, other: Iterable[_T], /) -> set[tuple[_KT_co, _VT_co] | _T]: ... class KeysView(MappingView, AbstractSet[_KT_co]): def __init__(self, mapping: Viewable[_KT_co]) -> None: ... # undocumented - def __and__(self, other: Iterable[Any]) -> set[_KT_co]: ... - def __rand__(self, other: Iterable[_T]) -> set[_T]: ... - def __contains__(self, key: object) -> bool: ... + def __and__(self, other: Iterable[Any], /) -> set[_KT_co]: ... + def __rand__(self, other: Iterable[_T], /) -> set[_T]: ... + def __contains__(self, key: object, /) -> bool: ... def __iter__(self) -> Iterator[_KT_co]: ... - def __or__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... - def __ror__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... - def __sub__(self, other: Iterable[Any]) -> set[_KT_co]: ... - def __rsub__(self, other: Iterable[_T]) -> set[_T]: ... - def __xor__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... - def __rxor__(self, other: Iterable[_T]) -> set[_KT_co | _T]: ... + def __or__(self, other: Iterable[_T], /) -> set[_KT_co | _T]: ... + def __ror__(self, other: Iterable[_T], /) -> set[_KT_co | _T]: ... + def __sub__(self, other: Iterable[Any], /) -> set[_KT_co]: ... + def __rsub__(self, other: Iterable[_T], /) -> set[_T]: ... + def __xor__(self, other: Iterable[_T], /) -> set[_KT_co | _T]: ... + def __rxor__(self, other: Iterable[_T], /) -> set[_KT_co | _T]: ... class ValuesView(MappingView, Collection[_VT_co]): def __init__(self, mapping: SupportsGetItemViewable[Any, _VT_co]) -> None: ... # undocumented - def __contains__(self, value: object) -> bool: ... + def __contains__(self, value: object, /) -> bool: ... def __iter__(self) -> Iterator[_VT_co]: ... # note for Mapping.get and MutableMapping.pop and MutableMapping.setdefault From 119ce3dbdf70f31237d46589cbb50b6461cbdb13 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Mon, 19 Jan 2026 22:38:25 +0100 Subject: [PATCH 2/2] updated comment --- stdlib/@tests/stubtest_allowlists/common.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/common.txt b/stdlib/@tests/stubtest_allowlists/common.txt index 2a186aa3f8c7..9d0633876cea 100644 --- a/stdlib/@tests/stubtest_allowlists/common.txt +++ b/stdlib/@tests/stubtest_allowlists/common.txt @@ -193,8 +193,8 @@ typing\.ByteString _collections_abc.Callable # Typing-related weirdness -# lots of methods are positional-or-keyword in implementation -# but this is unsafe as canonical types list/dist/set etc. only support positional args +# While the implementation in _collections_abc.py uses positional-or-keyword args, +# this is unsafe as canonical types list/dict/set etc. only support positional args. # See: https://github.com/python/typeshed/issues/14071 # See: https://github.com/python/cpython/issues/135312 _collections_abc.Mapping.get