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 len(self.data) - len(self._pending_removals)
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 for element in other:
122 def __ior__(self, other):
126 def difference(self, other):
128 newset.difference_update(other)
132 def difference_update(self, other):
134 def __isub__(self, other):
135 if self._pending_removals:
136 self._commit_removals()
140 self.data.difference_update(ref(item) for item in other)
143 def intersection(self, other):
144 return self.__class__(item for item in other if item in self)
145 __and__ = intersection
147 def intersection_update(self, other):
149 def __iand__(self, other):
150 if self._pending_removals:
151 self._commit_removals()
152 self.data.intersection_update(ref(item) for item in other)
155 def issubset(self, other):
156 return self.data.issubset(ref(item) for item in other)
159 def __lt__(self, other):
160 return self.data < set(ref(item) for item in other)
162 def issuperset(self, other):
163 return self.data.issuperset(ref(item) for item in other)
166 def __gt__(self, other):
167 return self.data > set(ref(item) for item in other)
169 def __eq__(self, other):
170 if not isinstance(other, self.__class__):
171 return NotImplemented
172 return self.data == set(ref(item) for item in other)
174 def __ne__(self, other):
175 opposite = self.__eq__(other)
176 if opposite is NotImplemented:
177 return NotImplemented
180 def symmetric_difference(self, other):
182 newset.symmetric_difference_update(other)
184 __xor__ = symmetric_difference
186 def symmetric_difference_update(self, other):
188 def __ixor__(self, other):
189 if self._pending_removals:
190 self._commit_removals()
194 self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
197 def union(self, other):
198 return self.__class__(e for s in (self, other) for e in s)
201 def isdisjoint(self, other):
202 return len(self.intersection(other)) == 0