diff --git a/src/sortedcontainers/sortedset.py b/src/sortedcontainers/sortedset.py index f2416bf..8301d80 100644 --- a/src/sortedcontainers/sortedset.py +++ b/src/sortedcontainers/sortedset.py @@ -599,9 +599,15 @@ def symmetric_difference_update(self, other): """ _set = self._set _list = self._list - _set.symmetric_difference_update(other) - _list.clear() - _list.update(_set) + candidate_set = _set.symmetric_difference(other) + candidate_list = SortedList(key=self._key) + candidate_list._reset(_list._load) + candidate_list.update(candidate_set) + # Validate keys and comparisons before changing either backing container. + # Preserve their identities because public methods are bound to them. + _set.clear() + _set.update(candidate_set) + _list.__dict__.update(candidate_list.__dict__) return self __ixor__ = symmetric_difference_update diff --git a/tests/test_sortedset_symmetric_difference_failure.py b/tests/test_sortedset_symmetric_difference_failure.py new file mode 100644 index 0000000..9008fdc --- /dev/null +++ b/tests/test_sortedset_symmetric_difference_failure.py @@ -0,0 +1,33 @@ +import pytest + +from sortedcontainers import SortedSet + + +@pytest.mark.parametrize('key', [None, int]) +@pytest.mark.parametrize('operation', ['method', 'ixor']) +def test_failed_symmetric_difference_preserves_membership_and_index(key, operation): + values = SortedSet([1, 2, 3], key=key) + values._reset(7) + bisect_left = values.bisect_left + isdisjoint = values.isdisjoint + incoming = iter([2, 4, 'invalid']) + + with pytest.raises((TypeError, ValueError)): + if operation == 'method': + values.symmetric_difference_update(incoming) + else: + values ^= incoming + + assert list(values) == [1, 2, 3] + assert len(values) == 3 + assert 'invalid' not in values + assert bisect_left(2) == 1 + assert isdisjoint([4]) + values._check() + + assert values.symmetric_difference_update(iter([2, 4])) is values + assert list(values) == [1, 3, 4] + assert bisect_left(4) == 2 + assert not isdisjoint([4]) + assert values._list._load == 7 + values._check()