blob: 2a27684324d80ab32f576040fb80ec666a8f7ff8 [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
Ethan Smith8ef87502020-04-13 21:54:40 -07006from types import GenericAlias
Raymond Hettinger93fa6082008-02-05 00:20:01 +00007
8__all__ = ['WeakSet']
9
Antoine Pitrouc1baa602010-01-08 17:54:23 +000010
11class _IterationGuard:
12 # This context manager registers itself in the current iterators of the
13 # weak container, such as to delay all removals until the context manager
14 # exits.
15 # This technique should be relatively thread-safe (since sets are).
16
17 def __init__(self, weakcontainer):
18 # Don't create cycles
19 self.weakcontainer = ref(weakcontainer)
20
21 def __enter__(self):
22 w = self.weakcontainer()
23 if w is not None:
24 w._iterating.add(self)
25 return self
26
27 def __exit__(self, e, t, b):
28 w = self.weakcontainer()
29 if w is not None:
30 s = w._iterating
31 s.remove(self)
32 if not s:
33 w._commit_removals()
34
35
Raymond Hettinger93fa6082008-02-05 00:20:01 +000036class WeakSet:
37 def __init__(self, data=None):
38 self.data = set()
39 def _remove(item, selfref=ref(self)):
40 self = selfref()
41 if self is not None:
Antoine Pitrouc1baa602010-01-08 17:54:23 +000042 if self._iterating:
43 self._pending_removals.append(item)
44 else:
45 self.data.discard(item)
Raymond Hettinger93fa6082008-02-05 00:20:01 +000046 self._remove = _remove
Antoine Pitrouc1baa602010-01-08 17:54:23 +000047 # A list of keys to be removed
48 self._pending_removals = []
49 self._iterating = set()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000050 if data is not None:
51 self.update(data)
52
Antoine Pitrouc1baa602010-01-08 17:54:23 +000053 def _commit_removals(self):
Miss Islington (bot)8aa64cc2021-08-28 11:09:21 -070054 pop = self._pending_removals.pop
Antoine Pitrouc1baa602010-01-08 17:54:23 +000055 discard = self.data.discard
Miss Islington (bot)8aa64cc2021-08-28 11:09:21 -070056 while True:
57 try:
58 item = pop()
59 except IndexError:
60 return
61 discard(item)
Antoine Pitrouc1baa602010-01-08 17:54:23 +000062
Raymond Hettinger93fa6082008-02-05 00:20:01 +000063 def __iter__(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000064 with _IterationGuard(self):
65 for itemref in self.data:
66 item = itemref()
67 if item is not None:
Antoine Pitrou320b3912013-12-18 00:28:36 +010068 # Caveat: the iterator will keep a strong reference to
69 # `item` until it is resumed or closed.
Antoine Pitrouc1baa602010-01-08 17:54:23 +000070 yield item
Raymond Hettinger93fa6082008-02-05 00:20:01 +000071
Georg Brandl9dba5d92008-05-18 16:27:29 +000072 def __len__(self):
Antoine Pitroubbe2f602012-03-01 16:26:35 +010073 return len(self.data) - len(self._pending_removals)
Georg Brandl9dba5d92008-05-18 16:27:29 +000074
Raymond Hettinger93fa6082008-02-05 00:20:01 +000075 def __contains__(self, item):
Georg Brandlf8de3fe2010-12-03 07:55:44 +000076 try:
77 wr = ref(item)
78 except TypeError:
79 return False
80 return wr in self.data
Raymond Hettinger93fa6082008-02-05 00:20:01 +000081
82 def __reduce__(self):
83 return (self.__class__, (list(self),),
84 getattr(self, '__dict__', None))
85
86 def add(self, item):
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.add(ref(item, self._remove))
90
91 def clear(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000092 if self._pending_removals:
93 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000094 self.data.clear()
95
96 def copy(self):
97 return self.__class__(self)
98
99 def pop(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000100 if self._pending_removals:
101 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000102 while True:
Georg Brandlbf93b042008-05-18 07:46:13 +0000103 try:
104 itemref = self.data.pop()
105 except KeyError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +0300106 raise KeyError('pop from empty WeakSet') from None
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000107 item = itemref()
108 if item is not None:
109 return item
110
111 def remove(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.remove(ref(item))
115
116 def discard(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000117 if self._pending_removals:
118 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000119 self.data.discard(ref(item))
120
121 def update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000122 if self._pending_removals:
123 self._commit_removals()
Antoine Pitroude89d4b2012-03-04 20:20:34 +0100124 for element in other:
125 self.add(element)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000126
Georg Brandl9dba5d92008-05-18 16:27:29 +0000127 def __ior__(self, other):
128 self.update(other)
129 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000130
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000131 def difference(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100132 newset = self.copy()
133 newset.difference_update(other)
134 return newset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000135 __sub__ = difference
136
137 def difference_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100138 self.__isub__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000139 def __isub__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000140 if self._pending_removals:
141 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000142 if self is other:
143 self.data.clear()
144 else:
145 self.data.difference_update(ref(item) for item in other)
146 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000147
148 def intersection(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100149 return self.__class__(item for item in other if item in self)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000150 __and__ = intersection
151
152 def intersection_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100153 self.__iand__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000154 def __iand__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000155 if self._pending_removals:
156 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000157 self.data.intersection_update(ref(item) for item in other)
158 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000159
160 def issubset(self, other):
161 return self.data.issubset(ref(item) for item in other)
Meador Inge653f9322012-03-04 22:15:38 -0600162 __le__ = issubset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000163
Meador Inge653f9322012-03-04 22:15:38 -0600164 def __lt__(self, other):
Jon Dufresne39726282017-05-18 07:35:54 -0700165 return self.data < set(map(ref, other))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000166
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000167 def issuperset(self, other):
168 return self.data.issuperset(ref(item) for item in other)
Meador Inge653f9322012-03-04 22:15:38 -0600169 __ge__ = issuperset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000170
Meador Inge653f9322012-03-04 22:15:38 -0600171 def __gt__(self, other):
Jon Dufresne39726282017-05-18 07:35:54 -0700172 return self.data > set(map(ref, other))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000173
174 def __eq__(self, other):
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000175 if not isinstance(other, self.__class__):
176 return NotImplemented
Jon Dufresne39726282017-05-18 07:35:54 -0700177 return self.data == set(map(ref, other))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000178
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000179 def symmetric_difference(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100180 newset = self.copy()
181 newset.symmetric_difference_update(other)
182 return newset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000183 __xor__ = symmetric_difference
184
185 def symmetric_difference_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100186 self.__ixor__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000187 def __ixor__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000188 if self._pending_removals:
189 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000190 if self is other:
191 self.data.clear()
192 else:
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100193 self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000194 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000195
196 def union(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100197 return self.__class__(e for s in (self, other) for e in s)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000198 __or__ = union
Georg Brandl9dba5d92008-05-18 16:27:29 +0000199
200 def isdisjoint(self, other):
201 return len(self.intersection(other)) == 0
Batuhan Taşkaya5ae1c842019-05-20 20:01:07 +0300202
203 def __repr__(self):
204 return repr(self.data)
Ethan Smith8ef87502020-04-13 21:54:40 -0700205
206 __class_getitem__ = classmethod(GenericAlias)