blob: 3de3bda695f5773bd0caf6a4db68ee005504dbef [file] [log] [blame]
Raymond Hettinger93fa6082008-02-05 00:20:01 +00001# 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.
4
5from _weakref import ref
6
7__all__ = ['WeakSet']
8
Antoine Pitrouc1baa602010-01-08 17:54:23 +00009
10class _IterationGuard:
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
13 # exits.
14 # This technique should be relatively thread-safe (since sets are).
15
16 def __init__(self, weakcontainer):
17 # Don't create cycles
18 self.weakcontainer = ref(weakcontainer)
19
20 def __enter__(self):
21 w = self.weakcontainer()
22 if w is not None:
23 w._iterating.add(self)
24 return self
25
26 def __exit__(self, e, t, b):
27 w = self.weakcontainer()
28 if w is not None:
29 s = w._iterating
30 s.remove(self)
31 if not s:
32 w._commit_removals()
33
34
Raymond Hettinger93fa6082008-02-05 00:20:01 +000035class WeakSet:
36 def __init__(self, data=None):
37 self.data = set()
38 def _remove(item, selfref=ref(self)):
39 self = selfref()
40 if self is not None:
Antoine Pitrouc1baa602010-01-08 17:54:23 +000041 if self._iterating:
42 self._pending_removals.append(item)
43 else:
44 self.data.discard(item)
Raymond Hettinger93fa6082008-02-05 00:20:01 +000045 self._remove = _remove
Antoine Pitrouc1baa602010-01-08 17:54:23 +000046 # A list of keys to be removed
47 self._pending_removals = []
48 self._iterating = set()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000049 if data is not None:
50 self.update(data)
51
Antoine Pitrouc1baa602010-01-08 17:54:23 +000052 def _commit_removals(self):
53 l = self._pending_removals
54 discard = self.data.discard
55 while l:
56 discard(l.pop())
57
Raymond Hettinger93fa6082008-02-05 00:20:01 +000058 def __iter__(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000059 with _IterationGuard(self):
60 for itemref in self.data:
61 item = itemref()
62 if item is not None:
63 yield item
Raymond Hettinger93fa6082008-02-05 00:20:01 +000064
Georg Brandl9dba5d92008-05-18 16:27:29 +000065 def __len__(self):
66 return sum(x() is not None for x in self.data)
67
Raymond Hettinger93fa6082008-02-05 00:20:01 +000068 def __contains__(self, item):
69 return ref(item) in self.data
70
71 def __reduce__(self):
72 return (self.__class__, (list(self),),
73 getattr(self, '__dict__', None))
74
75 def add(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000076 if self._pending_removals:
77 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000078 self.data.add(ref(item, self._remove))
79
80 def clear(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000081 if self._pending_removals:
82 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000083 self.data.clear()
84
85 def copy(self):
86 return self.__class__(self)
87
88 def pop(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000089 if self._pending_removals:
90 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000091 while True:
Georg Brandlbf93b042008-05-18 07:46:13 +000092 try:
93 itemref = self.data.pop()
94 except KeyError:
95 raise KeyError('pop from empty WeakSet')
Raymond Hettinger93fa6082008-02-05 00:20:01 +000096 item = itemref()
97 if item is not None:
98 return item
99
100 def remove(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000101 if self._pending_removals:
102 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000103 self.data.remove(ref(item))
104
105 def discard(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000106 if self._pending_removals:
107 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000108 self.data.discard(ref(item))
109
110 def update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000111 if self._pending_removals:
112 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000113 if isinstance(other, self.__class__):
114 self.data.update(other.data)
115 else:
116 for element in other:
117 self.add(element)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000118
Georg Brandl9dba5d92008-05-18 16:27:29 +0000119 def __ior__(self, other):
120 self.update(other)
121 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000122
123 # Helper functions for simple delegating methods.
124 def _apply(self, other, method):
125 if not isinstance(other, self.__class__):
126 other = self.__class__(other)
127 newdata = method(other.data)
128 newset = self.__class__()
129 newset.data = newdata
130 return newset
131
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000132 def difference(self, other):
133 return self._apply(other, self.data.difference)
134 __sub__ = difference
135
136 def difference_update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000137 if self._pending_removals:
138 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000139 if self is other:
140 self.data.clear()
141 else:
142 self.data.difference_update(ref(item) for item in other)
143 def __isub__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000144 if self._pending_removals:
145 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000146 if self is other:
147 self.data.clear()
148 else:
149 self.data.difference_update(ref(item) for item in other)
150 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000151
152 def intersection(self, other):
153 return self._apply(other, self.data.intersection)
154 __and__ = intersection
155
156 def intersection_update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000157 if self._pending_removals:
158 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000159 self.data.intersection_update(ref(item) for item in other)
160 def __iand__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000161 if self._pending_removals:
162 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000163 self.data.intersection_update(ref(item) for item in other)
164 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000165
166 def issubset(self, other):
167 return self.data.issubset(ref(item) for item in other)
168 __lt__ = issubset
169
Georg Brandl9dba5d92008-05-18 16:27:29 +0000170 def __le__(self, other):
171 return self.data <= set(ref(item) for item in other)
172
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000173 def issuperset(self, other):
174 return self.data.issuperset(ref(item) for item in other)
175 __gt__ = issuperset
176
Georg Brandl9dba5d92008-05-18 16:27:29 +0000177 def __ge__(self, other):
178 return self.data >= set(ref(item) for item in other)
179
180 def __eq__(self, other):
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000181 if not isinstance(other, self.__class__):
182 return NotImplemented
Georg Brandl9dba5d92008-05-18 16:27:29 +0000183 return self.data == set(ref(item) for item in other)
184
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000185 def symmetric_difference(self, other):
186 return self._apply(other, self.data.symmetric_difference)
187 __xor__ = symmetric_difference
188
189 def symmetric_difference_update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000190 if self._pending_removals:
191 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000192 if self is other:
193 self.data.clear()
194 else:
195 self.data.symmetric_difference_update(ref(item) for item in other)
196 def __ixor__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000197 if self._pending_removals:
198 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000199 if self is other:
200 self.data.clear()
201 else:
202 self.data.symmetric_difference_update(ref(item) for item in other)
203 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000204
205 def union(self, other):
Georg Brandlbf93b042008-05-18 07:46:13 +0000206 return self._apply(other, self.data.union)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000207 __or__ = union
Georg Brandl9dba5d92008-05-18 16:27:29 +0000208
209 def isdisjoint(self, other):
210 return len(self.intersection(other)) == 0