|  | # Access WeakSet through the weakref module. | 
|  | # This code is separated-out because it is needed | 
|  | # by abc.py to load everything else at startup. | 
|  |  | 
|  | from _weakref import ref | 
|  |  | 
|  | __all__ = ['WeakSet'] | 
|  |  | 
|  |  | 
|  | class _IterationGuard(object): | 
|  | # This context manager registers itself in the current iterators of the | 
|  | # weak container, such as to delay all removals until the context manager | 
|  | # exits. | 
|  | # This technique should be relatively thread-safe (since sets are). | 
|  |  | 
|  | def __init__(self, weakcontainer): | 
|  | # Don't create cycles | 
|  | self.weakcontainer = ref(weakcontainer) | 
|  |  | 
|  | def __enter__(self): | 
|  | w = self.weakcontainer() | 
|  | if w is not None: | 
|  | w._iterating.add(self) | 
|  | return self | 
|  |  | 
|  | def __exit__(self, e, t, b): | 
|  | w = self.weakcontainer() | 
|  | if w is not None: | 
|  | s = w._iterating | 
|  | s.remove(self) | 
|  | if not s: | 
|  | w._commit_removals() | 
|  |  | 
|  |  | 
|  | class WeakSet(object): | 
|  | 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._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): | 
|  | l = self._pending_removals | 
|  | discard = self.data.discard | 
|  | while l: | 
|  | discard(l.pop()) | 
|  |  | 
|  | 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 | 
|  |  | 
|  | def __len__(self): | 
|  | return len(self.data) - len(self._pending_removals) | 
|  |  | 
|  | def __contains__(self, item): | 
|  | try: | 
|  | wr = ref(item) | 
|  | except TypeError: | 
|  | return False | 
|  | return wr in self.data | 
|  |  | 
|  | def __reduce__(self): | 
|  | return (self.__class__, (list(self),), | 
|  | getattr(self, '__dict__', None)) | 
|  |  | 
|  | __hash__ = None | 
|  |  | 
|  | 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() | 
|  | except KeyError: | 
|  | raise KeyError('pop from empty WeakSet') | 
|  | item = itemref() | 
|  | if item is not None: | 
|  | 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) | 
|  |  | 
|  | def __ior__(self, other): | 
|  | self.update(other) | 
|  | return self | 
|  |  | 
|  | def difference(self, other): | 
|  | newset = self.copy() | 
|  | newset.difference_update(other) | 
|  | return newset | 
|  | __sub__ = difference | 
|  |  | 
|  | 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: | 
|  | self.data.difference_update(ref(item) for item in other) | 
|  | return self | 
|  |  | 
|  | def intersection(self, other): | 
|  | return self.__class__(item for item in other if item in self) | 
|  | __and__ = intersection | 
|  |  | 
|  | 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 | 
|  |  | 
|  | def issubset(self, other): | 
|  | return self.data.issubset(ref(item) for item in other) | 
|  | __le__ = issubset | 
|  |  | 
|  | def __lt__(self, other): | 
|  | return self.data < set(ref(item) for item in other) | 
|  |  | 
|  | def issuperset(self, other): | 
|  | return self.data.issuperset(ref(item) for item in other) | 
|  | __ge__ = issuperset | 
|  |  | 
|  | def __gt__(self, other): | 
|  | return self.data > set(ref(item) for item in other) | 
|  |  | 
|  | def __eq__(self, other): | 
|  | if not isinstance(other, self.__class__): | 
|  | return NotImplemented | 
|  | return self.data == set(ref(item) for item in other) | 
|  |  | 
|  | def __ne__(self, other): | 
|  | opposite = self.__eq__(other) | 
|  | if opposite is NotImplemented: | 
|  | return NotImplemented | 
|  | return not opposite | 
|  |  | 
|  | def symmetric_difference(self, other): | 
|  | newset = self.copy() | 
|  | newset.symmetric_difference_update(other) | 
|  | return newset | 
|  | __xor__ = symmetric_difference | 
|  |  | 
|  | 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: | 
|  | self.data.symmetric_difference_update(ref(item, self._remove) for item in other) | 
|  | return self | 
|  |  | 
|  | def union(self, other): | 
|  | return self.__class__(e for s in (self, other) for e in s) | 
|  | __or__ = union | 
|  |  | 
|  | def isdisjoint(self, other): | 
|  | return len(self.intersection(other)) == 0 |