1 # Access WeakSet through the weakref module.
2 # This code is separated-out because it is needed
3 # by abc.py to load everything else at startup.
5 from _weakref import ref
10 class _IterationGuard(object):
11 # This context manager registers itself in the current iterators of the
12 # weak container, such as to delay all removals until the context manager
14 # This technique should be relatively thread-safe (since sets are).
16 def __init__(self, weakcontainer):
18 self.weakcontainer = ref(weakcontainer)
21 w = self.weakcontainer()
23 w._iterating.add(self)
26 def __exit__(self, e, t, b):
27 w = self.weakcontainer()
35 class WeakSet(object):
36 def __init__(self, data=None):
38 def _remove(item, selfref=ref(self)):
42 self._pending_removals.append(item)
44 self.data.discard(item)
45 self._remove = _remove
46 # A list of keys to be removed
47 self._pending_removals = []
48 self._iterating = set()
52 def _commit_removals(self):
53 l = self._pending_removals
54 discard = self.data.discard
59 with _IterationGuard(self):
60 for itemref in self.data:
66 return sum(x() is not None for x in self.data)
68 def __contains__(self, item):
73 return wr in self.data
76 return (self.__class__, (list(self),),
77 getattr(self, '__dict__', None))
82 if self._pending_removals:
83 self._commit_removals()
84 self.data.add(ref(item, self._remove))
87 if self._pending_removals:
88 self._commit_removals()
92 return self.__class__(self)
95 if self._pending_removals:
96 self._commit_removals()
99 itemref = self.data.pop()
101 raise KeyError('pop from empty WeakSet')
106 def remove(self, item):
107 if self._pending_removals:
108 self._commit_removals()
109 self.data.remove(ref(item))
111 def discard(self, item):
112 if self._pending_removals:
113 self._commit_removals()
114 self.data.discard(ref(item))
116 def update(self, other):
117 if self._pending_removals:
118 self._commit_removals()
119 if isinstance(other, self.__class__):
120 self.data.update(other.data)
122 for element in other:
125 def __ior__(self, other):
129 # Helper functions for simple delegating methods.
130 def _apply(self, other, method):
131 if not isinstance(other, self.__class__):
132 other = self.__class__(other)
133 newdata = method(other.data)
134 newset = self.__class__()
135 newset.data = newdata
138 def difference(self, other):
139 return self._apply(other, self.data.difference)
142 def difference_update(self, other):
143 if self._pending_removals:
144 self._commit_removals()
148 self.data.difference_update(ref(item) for item in other)
149 def __isub__(self, other):
150 if self._pending_removals:
151 self._commit_removals()
155 self.data.difference_update(ref(item) for item in other)
158 def intersection(self, other):
159 return self._apply(other, self.data.intersection)
160 __and__ = intersection
162 def intersection_update(self, other):
163 if self._pending_removals:
164 self._commit_removals()
165 self.data.intersection_update(ref(item) for item in other)
166 def __iand__(self, other):
167 if self._pending_removals:
168 self._commit_removals()
169 self.data.intersection_update(ref(item) for item in other)
172 def issubset(self, other):
173 return self.data.issubset(ref(item) for item in other)
176 def __le__(self, other):
177 return self.data <= set(ref(item) for item in other)
179 def issuperset(self, other):
180 return self.data.issuperset(ref(item) for item in other)
183 def __ge__(self, other):
184 return self.data >= set(ref(item) for item in other)
186 def __eq__(self, other):
187 if not isinstance(other, self.__class__):
188 return NotImplemented
189 return self.data == set(ref(item) for item in other)
191 def symmetric_difference(self, other):
192 return self._apply(other, self.data.symmetric_difference)
193 __xor__ = symmetric_difference
195 def symmetric_difference_update(self, other):
196 if self._pending_removals:
197 self._commit_removals()
201 self.data.symmetric_difference_update(ref(item) for item in other)
202 def __ixor__(self, other):
203 if self._pending_removals:
204 self._commit_removals()
208 self.data.symmetric_difference_update(ref(item) for item in other)
211 def union(self, other):
212 return self._apply(other, self.data.union)
215 def isdisjoint(self, other):
216 return len(self.intersection(other)) == 0