blob: 627959b62433674e4d475cb5ee498d4b10dde325 [file] [log] [blame]
Michael Foorde6410c52010-03-29 20:04:23 +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
9
10class _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
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
35class WeakSet(object):
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:
41 if self._iterating:
42 self._pending_removals.append(item)
43 else:
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()
49 if data is not None:
50 self.update(data)
51
52 def _commit_removals(self):
53 l = self._pending_removals
54 discard = self.data.discard
55 while l:
56 discard(l.pop())
57
58 def __iter__(self):
59 with _IterationGuard(self):
60 for itemref in self.data:
61 item = itemref()
62 if item is not None:
Antoine Pitroubd4b6672013-12-18 00:28:36 +010063 # Caveat: the iterator will keep a strong reference to
64 # `item` until it is resumed or closed.
Michael Foorde6410c52010-03-29 20:04:23 +000065 yield item
66
67 def __len__(self):
Antoine Pitrouc56bca32012-03-01 16:26:35 +010068 return len(self.data) - len(self._pending_removals)
Michael Foorde6410c52010-03-29 20:04:23 +000069
70 def __contains__(self, item):
Georg Brandl52f83952011-02-25 10:39:23 +000071 try:
72 wr = ref(item)
73 except TypeError:
74 return False
75 return wr in self.data
Michael Foorde6410c52010-03-29 20:04:23 +000076
77 def __reduce__(self):
78 return (self.__class__, (list(self),),
79 getattr(self, '__dict__', None))
80
81 __hash__ = None
82
83 def add(self, item):
84 if self._pending_removals:
85 self._commit_removals()
86 self.data.add(ref(item, self._remove))
87
88 def clear(self):
89 if self._pending_removals:
90 self._commit_removals()
91 self.data.clear()
92
93 def copy(self):
94 return self.__class__(self)
95
96 def pop(self):
97 if self._pending_removals:
98 self._commit_removals()
99 while True:
100 try:
101 itemref = self.data.pop()
102 except KeyError:
103 raise KeyError('pop from empty WeakSet')
104 item = itemref()
105 if item is not None:
106 return item
107
108 def remove(self, item):
109 if self._pending_removals:
110 self._commit_removals()
111 self.data.remove(ref(item))
112
113 def discard(self, item):
114 if self._pending_removals:
115 self._commit_removals()
116 self.data.discard(ref(item))
117
118 def update(self, other):
119 if self._pending_removals:
120 self._commit_removals()
Antoine Pitrou859416e2012-03-04 20:20:34 +0100121 for element in other:
122 self.add(element)
Michael Foorde6410c52010-03-29 20:04:23 +0000123
124 def __ior__(self, other):
125 self.update(other)
126 return self
127
Michael Foorde6410c52010-03-29 20:04:23 +0000128 def difference(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100129 newset = self.copy()
130 newset.difference_update(other)
131 return newset
Michael Foorde6410c52010-03-29 20:04:23 +0000132 __sub__ = difference
133
134 def difference_update(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100135 self.__isub__(other)
Michael Foorde6410c52010-03-29 20:04:23 +0000136 def __isub__(self, other):
137 if self._pending_removals:
138 self._commit_removals()
139 if self is other:
140 self.data.clear()
141 else:
142 self.data.difference_update(ref(item) for item in other)
143 return self
144
145 def intersection(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100146 return self.__class__(item for item in other if item in self)
Michael Foorde6410c52010-03-29 20:04:23 +0000147 __and__ = intersection
148
149 def intersection_update(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100150 self.__iand__(other)
Michael Foorde6410c52010-03-29 20:04:23 +0000151 def __iand__(self, other):
152 if self._pending_removals:
153 self._commit_removals()
154 self.data.intersection_update(ref(item) for item in other)
155 return self
156
157 def issubset(self, other):
158 return self.data.issubset(ref(item) for item in other)
Meador Inge104f1892012-03-04 22:02:17 -0600159 __le__ = issubset
Michael Foorde6410c52010-03-29 20:04:23 +0000160
Meador Inge104f1892012-03-04 22:02:17 -0600161 def __lt__(self, other):
162 return self.data < set(ref(item) for item in other)
Michael Foorde6410c52010-03-29 20:04:23 +0000163
164 def issuperset(self, other):
165 return self.data.issuperset(ref(item) for item in other)
Meador Inge104f1892012-03-04 22:02:17 -0600166 __ge__ = issuperset
Michael Foorde6410c52010-03-29 20:04:23 +0000167
Meador Inge104f1892012-03-04 22:02:17 -0600168 def __gt__(self, other):
169 return self.data > set(ref(item) for item in other)
Michael Foorde6410c52010-03-29 20:04:23 +0000170
171 def __eq__(self, other):
172 if not isinstance(other, self.__class__):
173 return NotImplemented
174 return self.data == set(ref(item) for item in other)
175
Benjamin Peterson1cf48b42013-05-22 13:25:41 -0700176 def __ne__(self, other):
177 opposite = self.__eq__(other)
178 if opposite is NotImplemented:
179 return NotImplemented
180 return not opposite
181
Michael Foorde6410c52010-03-29 20:04:23 +0000182 def symmetric_difference(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100183 newset = self.copy()
184 newset.symmetric_difference_update(other)
185 return newset
Michael Foorde6410c52010-03-29 20:04:23 +0000186 __xor__ = symmetric_difference
187
188 def symmetric_difference_update(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100189 self.__ixor__(other)
Michael Foorde6410c52010-03-29 20:04:23 +0000190 def __ixor__(self, other):
191 if self._pending_removals:
192 self._commit_removals()
193 if self is other:
194 self.data.clear()
195 else:
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100196 self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
Michael Foorde6410c52010-03-29 20:04:23 +0000197 return self
198
199 def union(self, other):
Antoine Pitrou94c2d6df52012-03-04 20:47:05 +0100200 return self.__class__(e for s in (self, other) for e in s)
Michael Foorde6410c52010-03-29 20:04:23 +0000201 __or__ = union
202
203 def isdisjoint(self, other):
204 return len(self.intersection(other)) == 0