Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion stdlib/@tests/stubtest_allowlists/common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ _collections_abc.Mapping.__reversed__ # set to None at runtime for a better err
# Adding these reflected dunders to `typing.AbstractSet` causes a large number of false-positives. See #7414.
_collections_abc.Set.__rand__
_collections_abc.Set.__ror__
_collections_abc.Set.__rsub__
_collections_abc.Set.__rxor__

# Undocumented implementation details of a deprecated class
Expand Down
1 change: 0 additions & 1 deletion stdlib/@tests/stubtest_allowlists/py39.txt
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ collections.Mapping.__reversed__ # Set to None at runtime for a better error me
# Adding these reflected dunders to `typing.AbstractSet` causes a large number of false-positives. See #7414.
collections.Set.__rand__
collections.Set.__ror__
collections.Set.__rsub__
collections.Set.__rxor__

ssl.RAND_egd # Depends on the existence and flags of SSL
Expand Down
21 changes: 11 additions & 10 deletions stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1261,13 +1261,14 @@ class set(MutableSet[_T]):
def __len__(self) -> int: ...
def __contains__(self, o: object, /) -> bool: ...
def __iter__(self) -> Iterator[_T]: ...
def __and__(self, value: AbstractSet[object], /) -> set[_T]: ...
def __iand__(self, value: AbstractSet[object], /) -> Self: ...
def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...
def __and__(self, value: AbstractSet[object], /) -> set[_T]: ... # type: ignore[override,misc]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type ignores seem like a good indication that we shouldn't make this change: we shouldn't claim AbstractSet supports something if the concrete set class doesn't support it.

Copy link
Contributor Author

@Gatsik Gatsik Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't get what you mean: both set and collections.abc.Set have this method, but collections.abc.Set supports Iterable, while [C implementation of] set not, maybe the problem is that AbstractSet is used to represent collections.abc.Set and [in typeshed] set is a subclass of MutableSet

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an ABC like collections.abc.Set is used, a lot (most?) of the time the concrete runtime type that gets used will be builtins.set, just as when Sequence is used, the runtime type will usually be list or tuple, and with Mapping it will be dict. So if we claim that some operation is supported on collections.abc.Set, but it doesn't actually work with builtins.set, we'll fail to detect issues on code that uses builtins.set and uses these methods.

Of course, the tradeoff is that we emit false positives on some operations on other collections.abc.Set implementations, including those that rely on the mixins in the ABC. Maybe that's enough reason to make the change in this PR.

def __iand__(self, value: AbstractSet[object], /) -> Self: ... # type: ignore[override,misc]
def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[override,misc]
def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc]
def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ...
def __isub__(self, value: AbstractSet[object], /) -> Self: ...
def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...
def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ... # type: ignore[override,misc]
def __isub__(self, value: AbstractSet[object], /) -> Self: ... # type: ignore[override,misc]
def __rsub__(self, value: AbstractSet[_S], /) -> set[_S]: ... # type: ignore[override,misc]
def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[override,misc]
def __ixor__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc]
def __le__(self, value: AbstractSet[object], /) -> bool: ...
def __lt__(self, value: AbstractSet[object], /) -> bool: ...
Expand All @@ -1294,10 +1295,10 @@ class frozenset(AbstractSet[_T_co]):
def __len__(self) -> int: ...
def __contains__(self, o: object, /) -> bool: ...
def __iter__(self) -> Iterator[_T_co]: ...
def __and__(self, value: AbstractSet[_T_co], /) -> frozenset[_T_co]: ...
def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ...
def __sub__(self, value: AbstractSet[_T_co], /) -> frozenset[_T_co]: ...
def __xor__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ...
def __and__(self, value: AbstractSet[_T_co], /) -> frozenset[_T_co]: ... # type: ignore[override,misc]
def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... # type: ignore[override,misc]
def __sub__(self, value: AbstractSet[_T_co], /) -> frozenset[_T_co]: ... # type: ignore[override,misc]
def __xor__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... # type: ignore[override,misc]
def __le__(self, value: AbstractSet[object], /) -> bool: ...
def __lt__(self, value: AbstractSet[object], /) -> bool: ...
def __ge__(self, value: AbstractSet[object], /) -> bool: ...
Expand Down
14 changes: 7 additions & 7 deletions stdlib/multiprocessing/managers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ if sys.version_info >= (3, 14):
def __len__(self) -> int: ...
def __contains__(self, o: object, /) -> bool: ...
def __iter__(self) -> Iterator[_T]: ...
def __and__(self, value: AbstractSet[object], /) -> set[_T]: ...
def __iand__(self, value: AbstractSet[object], /) -> Self: ...
def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...
def __and__(self, value: AbstractSet[object], /) -> set[_T]: ... # type: ignore[override,misc]
def __iand__(self, value: AbstractSet[object], /) -> Self: ... # type: ignore[override,misc]
def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[override,misc]
def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc]
def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ...
def __isub__(self, value: AbstractSet[object], /) -> Self: ...
def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ...
def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ... # type: ignore[override,misc]
def __isub__(self, value: AbstractSet[object], /) -> Self: ... # type: ignore[override,misc]
def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[override,misc]
def __ixor__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc]
def __le__(self, value: AbstractSet[object], /) -> bool: ...
def __lt__(self, value: AbstractSet[object], /) -> bool: ...
Expand All @@ -162,7 +162,7 @@ if sys.version_info >= (3, 14):
def __eq__(self, value: object, /) -> bool: ...
def __rand__(self, value: AbstractSet[object], /) -> set[_T]: ...
def __ror__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[misc]
def __rsub__(self, value: AbstractSet[_T], /) -> set[_T]: ...
def __rsub__(self, value: AbstractSet[_T], /) -> set[_T]: ... # type: ignore[override,misc]
def __rxor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... # type: ignore[misc]
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...

Expand Down
17 changes: 9 additions & 8 deletions stdlib/typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -704,10 +704,11 @@ class AbstractSet(Collection[_T_co]):
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 __and__(self, other: Iterable[Any]) -> AbstractSet[_T_co]: ...
def __or__(self, other: Iterable[_T]) -> AbstractSet[_T_co | _T]: ...
def __sub__(self, other: Iterable[Any]) -> AbstractSet[_T_co]: ...
def __rsub__(self, other: Iterable[_T]) -> AbstractSet[_T]: ...
def __xor__(self, other: Iterable[_T]) -> AbstractSet[_T_co | _T]: ...
def __eq__(self, other: object) -> bool: ...
def isdisjoint(self, other: Iterable[Any]) -> bool: ...

Expand All @@ -720,10 +721,10 @@ class MutableSet(AbstractSet[_T]):
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 __ior__(self, it: Iterable[_T]) -> typing_extensions.Self: ... # type: ignore[override,misc]
def __iand__(self, it: Iterable[Any]) -> typing_extensions.Self: ...
def __ixor__(self, it: Iterable[_T]) -> typing_extensions.Self: ... # type: ignore[override,misc]
def __isub__(self, it: Iterable[Any]) -> typing_extensions.Self: ...

class MappingView(Sized):
__slots__ = ("_mapping",)
Expand Down
2 changes: 1 addition & 1 deletion stubs/boltons/boltons/setutils.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class IndexedSet(MutableSet[Any]):
# __sub__ = difference
# __xor__ = symmetric_difference
__rxor__ = symmetric_difference
def __rsub__(self, other: _RSub[_T_co]) -> _RSub[_T_co]: ...
def __rsub__(self, other: _RSub[_T_co]) -> _RSub[_T_co]: ... # type: ignore[override]
def update(self, *others: Iterable[Any]) -> None: ...
def intersection_update(self, *others: Iterable[Any]) -> None: ...
def difference_update(self, *others: Container[Any]) -> None: ...
Expand Down
Loading