blob: 7f9923c6341a54398e346d95f988222dbd8016b0 [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:
Antoine Pitrou320b3912013-12-18 00:28:36 +010063 # Caveat: the iterator will keep a strong reference to
64 # `item` until it is resumed or closed.
Antoine Pitrouc1baa602010-01-08 17:54:23 +000065 yield item
Raymond Hettinger93fa6082008-02-05 00:20:01 +000066
Georg Brandl9dba5d92008-05-18 16:27:29 +000067 def __len__(self):
Antoine Pitroubbe2f602012-03-01 16:26:35 +010068 return len(self.data) - len(self._pending_removals)
Georg Brandl9dba5d92008-05-18 16:27:29 +000069
Raymond Hettinger93fa6082008-02-05 00:20:01 +000070 def __contains__(self, item):
Georg Brandlf8de3fe2010-12-03 07:55:44 +000071 try:
72 wr = ref(item)
73 except TypeError:
74 return False
75 return wr in self.data
Raymond Hettinger93fa6082008-02-05 00:20:01 +000076
77 def __reduce__(self):
78 return (self.__class__, (list(self),),
79 getattr(self, '__dict__', None))
80
81 def add(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000082 if self._pending_removals:
83 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000084 self.data.add(ref(item, self._remove))
85
86 def clear(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000087 if self._pending_removals:
88 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000089 self.data.clear()
90
91 def copy(self):
92 return self.__class__(self)
93
94 def pop(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000095 if self._pending_removals:
96 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000097 while True:
Georg Brandlbf93b042008-05-18 07:46:13 +000098 try:
99 itemref = self.data.pop()
100 except KeyError:
101 raise KeyError('pop from empty WeakSet')
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000102 item = itemref()
103 if item is not None:
104 return item
105
106 def remove(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000107 if self._pending_removals:
108 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000109 self.data.remove(ref(item))
110
111 def discard(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000112 if self._pending_removals:
113 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000114 self.data.discard(ref(item))
115
116 def update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000117 if self._pending_removals:
118 self._commit_removals()
Antoine Pitroude89d4b2012-03-04 20:20:34 +0100119 for element in other:
120 self.add(element)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000121
Georg Brandl9dba5d92008-05-18 16:27:29 +0000122 def __ior__(self, other):
123 self.update(other)
124 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000125
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000126 def difference(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100127 newset = self.copy()
128 newset.difference_update(other)
129 return newset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000130 __sub__ = difference
131
132 def difference_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100133 self.__isub__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000134 def __isub__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000135 if self._pending_removals:
136 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000137 if self is other:
138 self.data.clear()
139 else:
140 self.data.difference_update(ref(item) for item in other)
141 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000142
143 def intersection(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100144 return self.__class__(item for item in other if item in self)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000145 __and__ = intersection
146
147 def intersection_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100148 self.__iand__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000149 def __iand__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000150 if self._pending_removals:
151 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000152 self.data.intersection_update(ref(item) for item in other)
153 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000154
155 def issubset(self, other):
156 return self.data.issubset(ref(item) for item in other)
Meador Inge653f9322012-03-04 22:15:38 -0600157 __le__ = issubset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000158
Meador Inge653f9322012-03-04 22:15:38 -0600159 def __lt__(self, other):
160 return self.data < set(ref(item) for item in other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000161
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000162 def issuperset(self, other):
163 return self.data.issuperset(ref(item) for item in other)
Meador Inge653f9322012-03-04 22:15:38 -0600164 __ge__ = issuperset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000165
Meador Inge653f9322012-03-04 22:15:38 -0600166 def __gt__(self, other):
167 return self.data > set(ref(item) for item in other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000168
169 def __eq__(self, other):
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000170 if not isinstance(other, self.__class__):
171 return NotImplemented
Georg Brandl9dba5d92008-05-18 16:27:29 +0000172 return self.data == set(ref(item) for item in other)
173
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000174 def symmetric_difference(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100175 newset = self.copy()
176 newset.symmetric_difference_update(other)
177 return newset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000178 __xor__ = symmetric_difference
179
180 def symmetric_difference_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100181 self.__ixor__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000182 def __ixor__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000183 if self._pending_removals:
184 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000185 if self is other:
186 self.data.clear()
187 else:
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100188 self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000189 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000190
191 def union(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100192 return self.__class__(e for s in (self, other) for e in s)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000193 __or__ = union
Georg Brandl9dba5d92008-05-18 16:27:29 +0000194
195 def isdisjoint(self, other):
196 return len(self.intersection(other)) == 0