Skip to content

Fix set xor return types for abstract sets - #16127

Merged
srittau merged 6 commits into
python:mainfrom
subotac:fix-set-xor-abstract-set
Oct 28, 2026
Merged

srittau merged 6 commits into
python:mainfrom
subotac:fix-set-xor-abstract-set

Conversation

@subotac

@subotac subotac commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #16115.

set.__xor__ and frozenset.__xor__ return NotImplemented for arbitrary collections.abc.Set implementations, allowing reflected dispatch to determine the result type. Preserve the concrete return type
for built-in set operands and return AbstractSet for the general case.

The new test covers both built-in left operands with an abstract set right operand.

@github-actions

This comment has been minimized.

@OutSquareCapital

Copy link
Copy Markdown

To be 100% correct the input type should be widened to Iterable. The default implementation of AbstractSet handles them

Comment thread stdlib/builtins.pyi Outdated
@overload
def __xor__(self, value: set[_S] | frozenset[_S], /) -> set[_T | _S]: ...
@overload
def __xor__(self, value: AbstractSet[_S], /) -> AbstractSet[_T | _S]: ...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems incorrect, set.__xor__ doesn't return this. It only accepts set and frozenset. xor with AbstractSets instead works through an __rxor__ method on those other sets.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated the builtin signatures to accept only set/frozenset and added AbstractSet.rxor for reflected dispatch; the regression test checks both paths. Could you take another look?

@OutSquareCapital OutSquareCapital Oct 28, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

My comment was completely ignored, but I'm insisting on the fact that AbstractSet inputs should be widened to Iterable. Most are currently incorrectly constrained to AbstractSet.

the actual code:

    def __and__(self, other):
        if not isinstance(other, Iterable):
            return NotImplemented
        return self._from_iterable(value for value in other if value in self)

    __rand__ = __and__

    def isdisjoint(self, other):
        """Return True if two sets have a null intersection."""
        for value in other:
            if value in self:
                return False
        return True

    def __or__(self, other):
        if not isinstance(other, Iterable):
            return NotImplemented
        chain = (e for s in (self, other) for e in s)
        return self._from_iterable(chain)

    __ror__ = __or__

    def __sub__(self, other):
        if not isinstance(other, Set):
            if not isinstance(other, Iterable):
                return NotImplemented
            other = self._from_iterable(other)
        return self._from_iterable(value for value in self if value not in other)

    def __rsub__(self, other):
        if not isinstance(other, Set):
            if not isinstance(other, Iterable):
                return NotImplemented
            other = self._from_iterable(other)
        return self._from_iterable(value for value in other if value not in self)

    def __xor__(self, other):
        if not isinstance(other, Set):
            if not isinstance(other, Iterable):
                return NotImplemented
            other = self._from_iterable(other)
        return (self - other) | (other - self)

    __rxor__ = __xor__

rxor having a signature different to xor don't make sense here. The rxor simply defer to xor.

@github-actions

This comment has been minimized.

@subotac
subotac requested a review from JelleZijlstra October 6, 2026 21:11
@subotac

subotac commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

The local conflict resolution preserves the newer reflected overloads and restores reflected XOR support for AbstractSet The focused typing checks pass. The update has not been pushed yet

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@srittau srittau left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@srittau
srittau merged commit 1f268d9 into python:main Oct 28, 2026
91 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

frozenset.__xor__ signature is wrong

4 participants