diff --git a/Lib/_weakrefset.py b/Lib/_weakrefset.py index 489eec714e0..2071755d71d 100644 --- a/Lib/_weakrefset.py +++ b/Lib/_weakrefset.py @@ -36,41 +36,26 @@ class _IterationGuard: class WeakSet: def __init__(self, data=None): self.data = set() + def _remove(item, selfref=ref(self)): self = selfref() if self is not None: - if self._iterating: - self._pending_removals.append(item) - else: - self.data.discard(item) + self.data.discard(item) + self._remove = _remove - # A list of keys to be removed - self._pending_removals = [] - self._iterating = set() if data is not None: self.update(data) - def _commit_removals(self): - pop = self._pending_removals.pop - discard = self.data.discard - while True: - try: - item = pop() - except IndexError: - return - discard(item) - def __iter__(self): - with _IterationGuard(self): - for itemref in self.data: - item = itemref() - if item is not None: - # Caveat: the iterator will keep a strong reference to - # `item` until it is resumed or closed. - yield item + for itemref in self.data.copy(): + item = itemref() + if item is not None: + # Caveat: the iterator will keep a strong reference to + # `item` until it is resumed or closed. + yield item def __len__(self): - return len(self.data) - len(self._pending_removals) + return len(self.data) def __contains__(self, item): try: @@ -83,21 +68,15 @@ class WeakSet: return self.__class__, (list(self),), self.__getstate__() def add(self, item): - if self._pending_removals: - self._commit_removals() self.data.add(ref(item, self._remove)) def clear(self): - if self._pending_removals: - self._commit_removals() self.data.clear() def copy(self): return self.__class__(self) def pop(self): - if self._pending_removals: - self._commit_removals() while True: try: itemref = self.data.pop() @@ -108,18 +87,12 @@ class WeakSet: return item def remove(self, item): - if self._pending_removals: - self._commit_removals() self.data.remove(ref(item)) def discard(self, item): - if self._pending_removals: - self._commit_removals() self.data.discard(ref(item)) def update(self, other): - if self._pending_removals: - self._commit_removals() for element in other: self.add(element) @@ -136,8 +109,6 @@ class WeakSet: def difference_update(self, other): self.__isub__(other) def __isub__(self, other): - if self._pending_removals: - self._commit_removals() if self is other: self.data.clear() else: @@ -151,8 +122,6 @@ class WeakSet: def intersection_update(self, other): self.__iand__(other) def __iand__(self, other): - if self._pending_removals: - self._commit_removals() self.data.intersection_update(ref(item) for item in other) return self @@ -184,8 +153,6 @@ class WeakSet: def symmetric_difference_update(self, other): self.__ixor__(other) def __ixor__(self, other): - if self._pending_removals: - self._commit_removals() if self is other: self.data.clear() else: diff --git a/Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst b/Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst new file mode 100644 index 00000000000..74cbdd55135 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst @@ -0,0 +1 @@ +Make :class:`weakref.WeakSet` safe against concurrent mutations while it is being iterated. Patch by Kumar Aditya.