From 831c28219432e0e0371029ae61956f159c32e949 Mon Sep 17 00:00:00 2001 From: subotac <73706465+subotac@users.noreply.github.com> Date: Fri, 31 Jul 2026 21:17:38 +0300 Subject: [PATCH 1/3] Fix set xor return types for abstract sets --- stdlib/@tests/test_cases/builtins/check_set.py | 8 ++++++++ stdlib/builtins.pyi | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/stdlib/@tests/test_cases/builtins/check_set.py b/stdlib/@tests/test_cases/builtins/check_set.py index 89cb8683bfe1..0cd429299ea5 100644 --- a/stdlib/@tests/test_cases/builtins/check_set.py +++ b/stdlib/@tests/test_cases/builtins/check_set.py @@ -1,3 +1,4 @@ +from collections.abc import Set as AbstractSet from typing_extensions import Literal, assert_type @@ -55,3 +56,10 @@ def test_frozenset_interface(s: frozenset[Literal["foo", "bar"]], y: frozenset[s assert_type(s & y, frozenset[Literal["foo", "bar"]]) assert_type(s | y, frozenset[str]) assert_type(s ^ y, frozenset[str]) + + +def test_xor_with_abstract_set( + s: set[Literal["foo", "bar"]], frozen: frozenset[Literal["foo", "bar"]], other: AbstractSet[str] +) -> None: + assert_type(s ^ other, AbstractSet[str]) + assert_type(frozen ^ other, AbstractSet[str]) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 3c549d97316c..16c4a06062ba 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1460,7 +1460,12 @@ class set(MutableSet[_T]): def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] def __sub__(self, value: AbstractSet[object], /) -> set[_T]: ... def __isub__(self, value: AbstractSet[object], /) -> Self: ... - def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... + + @overload + def __xor__(self, value: set[_S] | frozenset[_S], /) -> set[_T | _S]: ... + @overload + def __xor__(self, value: AbstractSet[_S], /) -> AbstractSet[_T | _S]: ... + 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: ... @@ -1491,7 +1496,12 @@ class frozenset(AbstractSet[_T_co]): def __and__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... def __sub__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... - def __xor__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... + + @overload + def __xor__(self, value: set[_S] | frozenset[_S], /) -> frozenset[_T_co | _S]: ... + @overload + def __xor__(self, value: AbstractSet[_S], /) -> AbstractSet[_T_co | _S]: ... + def __le__(self, value: AbstractSet[object], /) -> bool: ... def __lt__(self, value: AbstractSet[object], /) -> bool: ... def __ge__(self, value: AbstractSet[object], /) -> bool: ... From 39e1136d035a6b715a453fda15b708bf46c2b779 Mon Sep 17 00:00:00 2001 From: subotac <73706465+subotac@users.noreply.github.com> Date: Sun, 13 Sep 2026 18:24:04 +0300 Subject: [PATCH 2/3] Correct set xor signatures for abstract sets --- stdlib/@tests/test_cases/builtins/check_set.py | 4 ++++ stdlib/builtins.pyi | 12 ++++-------- stdlib/typing.pyi | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/stdlib/@tests/test_cases/builtins/check_set.py b/stdlib/@tests/test_cases/builtins/check_set.py index 0cd429299ea5..246a4ac95295 100644 --- a/stdlib/@tests/test_cases/builtins/check_set.py +++ b/stdlib/@tests/test_cases/builtins/check_set.py @@ -61,5 +61,9 @@ def test_frozenset_interface(s: frozenset[Literal["foo", "bar"]], y: frozenset[s def test_xor_with_abstract_set( s: set[Literal["foo", "bar"]], frozen: frozenset[Literal["foo", "bar"]], other: AbstractSet[str] ) -> None: + s.__xor__(other) # type: ignore + frozen.__xor__(other) # type: ignore + assert_type(other.__rxor__(s), AbstractSet[str]) + assert_type(other.__rxor__(frozen), AbstractSet[str]) assert_type(s ^ other, AbstractSet[str]) assert_type(frozen ^ other, AbstractSet[str]) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 16c4a06062ba..12dd72f90e05 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1461,10 +1461,8 @@ class set(MutableSet[_T]): def __sub__(self, value: AbstractSet[object], /) -> set[_T]: ... def __isub__(self, value: AbstractSet[object], /) -> Self: ... - @overload - def __xor__(self, value: set[_S] | frozenset[_S], /) -> set[_T | _S]: ... - @overload - def __xor__(self, value: AbstractSet[_S], /) -> AbstractSet[_T | _S]: ... + def __xor__(self, value: set[_S] | frozenset[_S], /) -> set[_T | _S]: ... # type: ignore[override] + def __rxor__(self, value: set[_S] | frozenset[_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: ... @@ -1497,10 +1495,8 @@ class frozenset(AbstractSet[_T_co]): def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... def __sub__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... - @overload - def __xor__(self, value: set[_S] | frozenset[_S], /) -> frozenset[_T_co | _S]: ... - @overload - def __xor__(self, value: AbstractSet[_S], /) -> AbstractSet[_T_co | _S]: ... + def __xor__(self, value: set[_S] | frozenset[_S], /) -> frozenset[_T_co | _S]: ... # type: ignore[override] + def __rxor__(self, value: set[_S] | frozenset[_S], /) -> frozenset[_T_co | _S]: ... # type: ignore[override,misc] def __le__(self, value: AbstractSet[object], /) -> bool: ... def __lt__(self, value: AbstractSet[object], /) -> bool: ... diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 35031a27445b..88f18250dcfe 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -742,6 +742,7 @@ class AbstractSet(Collection[_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 __rxor__(self, other: AbstractSet[_T], /) -> AbstractSet[_T_co | _T]: ... def __eq__(self, other: object, /) -> bool: ... def isdisjoint(self, other: Iterable[Any], /) -> bool: ... From 94a32b325beac49263daf837fa933ec96de90c7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 13 Sep 2026 15:31:32 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/builtins.pyi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 12dd72f90e05..cd2051710b60 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1460,10 +1460,8 @@ class set(MutableSet[_T]): def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] def __sub__(self, value: AbstractSet[object], /) -> set[_T]: ... def __isub__(self, value: AbstractSet[object], /) -> Self: ... - def __xor__(self, value: set[_S] | frozenset[_S], /) -> set[_T | _S]: ... # type: ignore[override] def __rxor__(self, value: set[_S] | frozenset[_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: ... @@ -1494,10 +1492,8 @@ class frozenset(AbstractSet[_T_co]): def __and__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... def __sub__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... - def __xor__(self, value: set[_S] | frozenset[_S], /) -> frozenset[_T_co | _S]: ... # type: ignore[override] def __rxor__(self, value: set[_S] | frozenset[_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: ...