1*cda5da8dSAndroid Build Coastguard Worker# Access WeakSet through the weakref module. 2*cda5da8dSAndroid Build Coastguard Worker# This code is separated-out because it is needed 3*cda5da8dSAndroid Build Coastguard Worker# by abc.py to load everything else at startup. 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Workerfrom _weakref import ref 6*cda5da8dSAndroid Build Coastguard Workerfrom types import GenericAlias 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Worker__all__ = ['WeakSet'] 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Worker 11*cda5da8dSAndroid Build Coastguard Workerclass _IterationGuard: 12*cda5da8dSAndroid Build Coastguard Worker # This context manager registers itself in the current iterators of the 13*cda5da8dSAndroid Build Coastguard Worker # weak container, such as to delay all removals until the context manager 14*cda5da8dSAndroid Build Coastguard Worker # exits. 15*cda5da8dSAndroid Build Coastguard Worker # This technique should be relatively thread-safe (since sets are). 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Worker def __init__(self, weakcontainer): 18*cda5da8dSAndroid Build Coastguard Worker # Don't create cycles 19*cda5da8dSAndroid Build Coastguard Worker self.weakcontainer = ref(weakcontainer) 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 22*cda5da8dSAndroid Build Coastguard Worker w = self.weakcontainer() 23*cda5da8dSAndroid Build Coastguard Worker if w is not None: 24*cda5da8dSAndroid Build Coastguard Worker w._iterating.add(self) 25*cda5da8dSAndroid Build Coastguard Worker return self 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, e, t, b): 28*cda5da8dSAndroid Build Coastguard Worker w = self.weakcontainer() 29*cda5da8dSAndroid Build Coastguard Worker if w is not None: 30*cda5da8dSAndroid Build Coastguard Worker s = w._iterating 31*cda5da8dSAndroid Build Coastguard Worker s.remove(self) 32*cda5da8dSAndroid Build Coastguard Worker if not s: 33*cda5da8dSAndroid Build Coastguard Worker w._commit_removals() 34*cda5da8dSAndroid Build Coastguard Worker 35*cda5da8dSAndroid Build Coastguard Worker 36*cda5da8dSAndroid Build Coastguard Workerclass WeakSet: 37*cda5da8dSAndroid Build Coastguard Worker def __init__(self, data=None): 38*cda5da8dSAndroid Build Coastguard Worker self.data = set() 39*cda5da8dSAndroid Build Coastguard Worker def _remove(item, selfref=ref(self)): 40*cda5da8dSAndroid Build Coastguard Worker self = selfref() 41*cda5da8dSAndroid Build Coastguard Worker if self is not None: 42*cda5da8dSAndroid Build Coastguard Worker if self._iterating: 43*cda5da8dSAndroid Build Coastguard Worker self._pending_removals.append(item) 44*cda5da8dSAndroid Build Coastguard Worker else: 45*cda5da8dSAndroid Build Coastguard Worker self.data.discard(item) 46*cda5da8dSAndroid Build Coastguard Worker self._remove = _remove 47*cda5da8dSAndroid Build Coastguard Worker # A list of keys to be removed 48*cda5da8dSAndroid Build Coastguard Worker self._pending_removals = [] 49*cda5da8dSAndroid Build Coastguard Worker self._iterating = set() 50*cda5da8dSAndroid Build Coastguard Worker if data is not None: 51*cda5da8dSAndroid Build Coastguard Worker self.update(data) 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker def _commit_removals(self): 54*cda5da8dSAndroid Build Coastguard Worker pop = self._pending_removals.pop 55*cda5da8dSAndroid Build Coastguard Worker discard = self.data.discard 56*cda5da8dSAndroid Build Coastguard Worker while True: 57*cda5da8dSAndroid Build Coastguard Worker try: 58*cda5da8dSAndroid Build Coastguard Worker item = pop() 59*cda5da8dSAndroid Build Coastguard Worker except IndexError: 60*cda5da8dSAndroid Build Coastguard Worker return 61*cda5da8dSAndroid Build Coastguard Worker discard(item) 62*cda5da8dSAndroid Build Coastguard Worker 63*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 64*cda5da8dSAndroid Build Coastguard Worker with _IterationGuard(self): 65*cda5da8dSAndroid Build Coastguard Worker for itemref in self.data: 66*cda5da8dSAndroid Build Coastguard Worker item = itemref() 67*cda5da8dSAndroid Build Coastguard Worker if item is not None: 68*cda5da8dSAndroid Build Coastguard Worker # Caveat: the iterator will keep a strong reference to 69*cda5da8dSAndroid Build Coastguard Worker # `item` until it is resumed or closed. 70*cda5da8dSAndroid Build Coastguard Worker yield item 71*cda5da8dSAndroid Build Coastguard Worker 72*cda5da8dSAndroid Build Coastguard Worker def __len__(self): 73*cda5da8dSAndroid Build Coastguard Worker return len(self.data) - len(self._pending_removals) 74*cda5da8dSAndroid Build Coastguard Worker 75*cda5da8dSAndroid Build Coastguard Worker def __contains__(self, item): 76*cda5da8dSAndroid Build Coastguard Worker try: 77*cda5da8dSAndroid Build Coastguard Worker wr = ref(item) 78*cda5da8dSAndroid Build Coastguard Worker except TypeError: 79*cda5da8dSAndroid Build Coastguard Worker return False 80*cda5da8dSAndroid Build Coastguard Worker return wr in self.data 81*cda5da8dSAndroid Build Coastguard Worker 82*cda5da8dSAndroid Build Coastguard Worker def __reduce__(self): 83*cda5da8dSAndroid Build Coastguard Worker return self.__class__, (list(self),), self.__getstate__() 84*cda5da8dSAndroid Build Coastguard Worker 85*cda5da8dSAndroid Build Coastguard Worker def add(self, item): 86*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 87*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 88*cda5da8dSAndroid Build Coastguard Worker self.data.add(ref(item, self._remove)) 89*cda5da8dSAndroid Build Coastguard Worker 90*cda5da8dSAndroid Build Coastguard Worker def clear(self): 91*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 92*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 93*cda5da8dSAndroid Build Coastguard Worker self.data.clear() 94*cda5da8dSAndroid Build Coastguard Worker 95*cda5da8dSAndroid Build Coastguard Worker def copy(self): 96*cda5da8dSAndroid Build Coastguard Worker return self.__class__(self) 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker def pop(self): 99*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 100*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 101*cda5da8dSAndroid Build Coastguard Worker while True: 102*cda5da8dSAndroid Build Coastguard Worker try: 103*cda5da8dSAndroid Build Coastguard Worker itemref = self.data.pop() 104*cda5da8dSAndroid Build Coastguard Worker except KeyError: 105*cda5da8dSAndroid Build Coastguard Worker raise KeyError('pop from empty WeakSet') from None 106*cda5da8dSAndroid Build Coastguard Worker item = itemref() 107*cda5da8dSAndroid Build Coastguard Worker if item is not None: 108*cda5da8dSAndroid Build Coastguard Worker return item 109*cda5da8dSAndroid Build Coastguard Worker 110*cda5da8dSAndroid Build Coastguard Worker def remove(self, item): 111*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 112*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 113*cda5da8dSAndroid Build Coastguard Worker self.data.remove(ref(item)) 114*cda5da8dSAndroid Build Coastguard Worker 115*cda5da8dSAndroid Build Coastguard Worker def discard(self, item): 116*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 117*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 118*cda5da8dSAndroid Build Coastguard Worker self.data.discard(ref(item)) 119*cda5da8dSAndroid Build Coastguard Worker 120*cda5da8dSAndroid Build Coastguard Worker def update(self, other): 121*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 122*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 123*cda5da8dSAndroid Build Coastguard Worker for element in other: 124*cda5da8dSAndroid Build Coastguard Worker self.add(element) 125*cda5da8dSAndroid Build Coastguard Worker 126*cda5da8dSAndroid Build Coastguard Worker def __ior__(self, other): 127*cda5da8dSAndroid Build Coastguard Worker self.update(other) 128*cda5da8dSAndroid Build Coastguard Worker return self 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Worker def difference(self, other): 131*cda5da8dSAndroid Build Coastguard Worker newset = self.copy() 132*cda5da8dSAndroid Build Coastguard Worker newset.difference_update(other) 133*cda5da8dSAndroid Build Coastguard Worker return newset 134*cda5da8dSAndroid Build Coastguard Worker __sub__ = difference 135*cda5da8dSAndroid Build Coastguard Worker 136*cda5da8dSAndroid Build Coastguard Worker def difference_update(self, other): 137*cda5da8dSAndroid Build Coastguard Worker self.__isub__(other) 138*cda5da8dSAndroid Build Coastguard Worker def __isub__(self, other): 139*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 140*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 141*cda5da8dSAndroid Build Coastguard Worker if self is other: 142*cda5da8dSAndroid Build Coastguard Worker self.data.clear() 143*cda5da8dSAndroid Build Coastguard Worker else: 144*cda5da8dSAndroid Build Coastguard Worker self.data.difference_update(ref(item) for item in other) 145*cda5da8dSAndroid Build Coastguard Worker return self 146*cda5da8dSAndroid Build Coastguard Worker 147*cda5da8dSAndroid Build Coastguard Worker def intersection(self, other): 148*cda5da8dSAndroid Build Coastguard Worker return self.__class__(item for item in other if item in self) 149*cda5da8dSAndroid Build Coastguard Worker __and__ = intersection 150*cda5da8dSAndroid Build Coastguard Worker 151*cda5da8dSAndroid Build Coastguard Worker def intersection_update(self, other): 152*cda5da8dSAndroid Build Coastguard Worker self.__iand__(other) 153*cda5da8dSAndroid Build Coastguard Worker def __iand__(self, other): 154*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 155*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 156*cda5da8dSAndroid Build Coastguard Worker self.data.intersection_update(ref(item) for item in other) 157*cda5da8dSAndroid Build Coastguard Worker return self 158*cda5da8dSAndroid Build Coastguard Worker 159*cda5da8dSAndroid Build Coastguard Worker def issubset(self, other): 160*cda5da8dSAndroid Build Coastguard Worker return self.data.issubset(ref(item) for item in other) 161*cda5da8dSAndroid Build Coastguard Worker __le__ = issubset 162*cda5da8dSAndroid Build Coastguard Worker 163*cda5da8dSAndroid Build Coastguard Worker def __lt__(self, other): 164*cda5da8dSAndroid Build Coastguard Worker return self.data < set(map(ref, other)) 165*cda5da8dSAndroid Build Coastguard Worker 166*cda5da8dSAndroid Build Coastguard Worker def issuperset(self, other): 167*cda5da8dSAndroid Build Coastguard Worker return self.data.issuperset(ref(item) for item in other) 168*cda5da8dSAndroid Build Coastguard Worker __ge__ = issuperset 169*cda5da8dSAndroid Build Coastguard Worker 170*cda5da8dSAndroid Build Coastguard Worker def __gt__(self, other): 171*cda5da8dSAndroid Build Coastguard Worker return self.data > set(map(ref, other)) 172*cda5da8dSAndroid Build Coastguard Worker 173*cda5da8dSAndroid Build Coastguard Worker def __eq__(self, other): 174*cda5da8dSAndroid Build Coastguard Worker if not isinstance(other, self.__class__): 175*cda5da8dSAndroid Build Coastguard Worker return NotImplemented 176*cda5da8dSAndroid Build Coastguard Worker return self.data == set(map(ref, other)) 177*cda5da8dSAndroid Build Coastguard Worker 178*cda5da8dSAndroid Build Coastguard Worker def symmetric_difference(self, other): 179*cda5da8dSAndroid Build Coastguard Worker newset = self.copy() 180*cda5da8dSAndroid Build Coastguard Worker newset.symmetric_difference_update(other) 181*cda5da8dSAndroid Build Coastguard Worker return newset 182*cda5da8dSAndroid Build Coastguard Worker __xor__ = symmetric_difference 183*cda5da8dSAndroid Build Coastguard Worker 184*cda5da8dSAndroid Build Coastguard Worker def symmetric_difference_update(self, other): 185*cda5da8dSAndroid Build Coastguard Worker self.__ixor__(other) 186*cda5da8dSAndroid Build Coastguard Worker def __ixor__(self, other): 187*cda5da8dSAndroid Build Coastguard Worker if self._pending_removals: 188*cda5da8dSAndroid Build Coastguard Worker self._commit_removals() 189*cda5da8dSAndroid Build Coastguard Worker if self is other: 190*cda5da8dSAndroid Build Coastguard Worker self.data.clear() 191*cda5da8dSAndroid Build Coastguard Worker else: 192*cda5da8dSAndroid Build Coastguard Worker self.data.symmetric_difference_update(ref(item, self._remove) for item in other) 193*cda5da8dSAndroid Build Coastguard Worker return self 194*cda5da8dSAndroid Build Coastguard Worker 195*cda5da8dSAndroid Build Coastguard Worker def union(self, other): 196*cda5da8dSAndroid Build Coastguard Worker return self.__class__(e for s in (self, other) for e in s) 197*cda5da8dSAndroid Build Coastguard Worker __or__ = union 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker def isdisjoint(self, other): 200*cda5da8dSAndroid Build Coastguard Worker return len(self.intersection(other)) == 0 201*cda5da8dSAndroid Build Coastguard Worker 202*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 203*cda5da8dSAndroid Build Coastguard Worker return repr(self.data) 204*cda5da8dSAndroid Build Coastguard Worker 205*cda5da8dSAndroid Build Coastguard Worker __class_getitem__ = classmethod(GenericAlias) 206