| # 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: | 
 |                     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 |