blob: b267780f0ced73087c8670473fdf4e7fe22ff193 [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):
54 l = self._pending_removals
55 discard = self.data.discard
56 while l:
57 discard(l.pop())
58
Raymond Hettinger93fa6082008-02-05 00:20:01 +000059 def __iter__(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000060 with _IterationGuard(self):
61 for itemref in self.data:
62 item = itemref()
63 if item is not None:
Antoine Pitrou320b3912013-12-18 00:28:36 +010064 # Caveat: the iterator will keep a strong reference to
65 # `item` until it is resumed or closed.
Antoine Pitrouc1baa602010-01-08 17:54:23 +000066 yield item
Raymond Hettinger93fa6082008-02-05 00:20:01 +000067
Georg Brandl9dba5d92008-05-18 16:27:29 +000068 def __len__(self):
Antoine Pitroubbe2f602012-03-01 16:26:35 +010069 return len(self.data) - len(self._pending_removals)
Georg Brandl9dba5d92008-05-18 16:27:29 +000070
Raymond Hettinger93fa6082008-02-05 00:20:01 +000071 def __contains__(self, item):
Georg Brandlf8de3fe2010-12-03 07:55:44 +000072 try:
73 wr = ref(item)
74 except TypeError:
75 return False
76 return wr in self.data
Raymond Hettinger93fa6082008-02-05 00:20:01 +000077
78 def __reduce__(self):
79 return (self.__class__, (list(self),),
80 getattr(self, '__dict__', None))
81
82 def add(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000083 if self._pending_removals:
84 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000085 self.data.add(ref(item, self._remove))
86
87 def clear(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000088 if self._pending_removals:
89 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000090 self.data.clear()
91
92 def copy(self):
93 return self.__class__(self)
94
95 def pop(self):
Antoine Pitrouc1baa602010-01-08 17:54:23 +000096 if self._pending_removals:
97 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +000098 while True:
Georg Brandlbf93b042008-05-18 07:46:13 +000099 try:
100 itemref = self.data.pop()
101 except KeyError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +0300102 raise KeyError('pop from empty WeakSet') from None
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000103 item = itemref()
104 if item is not None:
105 return item
106
107 def remove(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000108 if self._pending_removals:
109 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000110 self.data.remove(ref(item))
111
112 def discard(self, item):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000113 if self._pending_removals:
114 self._commit_removals()
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000115 self.data.discard(ref(item))
116
117 def update(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000118 if self._pending_removals:
119 self._commit_removals()
Antoine Pitroude89d4b2012-03-04 20:20:34 +0100120 for element in other:
121 self.add(element)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000122
Georg Brandl9dba5d92008-05-18 16:27:29 +0000123 def __ior__(self, other):
124 self.update(other)
125 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000126
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000127 def difference(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100128 newset = self.copy()
129 newset.difference_update(other)
130 return newset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000131 __sub__ = difference
132
133 def difference_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100134 self.__isub__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000135 def __isub__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000136 if self._pending_removals:
137 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000138 if self is other:
139 self.data.clear()
140 else:
141 self.data.difference_update(ref(item) for item in other)
142 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000143
144 def intersection(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100145 return self.__class__(item for item in other if item in self)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000146 __and__ = intersection
147
148 def intersection_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100149 self.__iand__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000150 def __iand__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000151 if self._pending_removals:
152 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000153 self.data.intersection_update(ref(item) for item in other)
154 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000155
156 def issubset(self, other):
157 return self.data.issubset(ref(item) for item in other)
Meador Inge653f9322012-03-04 22:15:38 -0600158 __le__ = issubset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000159
Meador Inge653f9322012-03-04 22:15:38 -0600160 def __lt__(self, other):
Jon Dufresne39726282017-05-18 07:35:54 -0700161 return self.data < set(map(ref, other))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000162
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000163 def issuperset(self, other):
164 return self.data.issuperset(ref(item) for item in other)
Meador Inge653f9322012-03-04 22:15:38 -0600165 __ge__ = issuperset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000166
Meador Inge653f9322012-03-04 22:15:38 -0600167 def __gt__(self, other):
Jon Dufresne39726282017-05-18 07:35:54 -0700168 return self.data > set(map(ref, other))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000169
170 def __eq__(self, other):
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000171 if not isinstance(other, self.__class__):
172 return NotImplemented
Jon Dufresne39726282017-05-18 07:35:54 -0700173 return self.data == set(map(ref, other))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000174
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000175 def symmetric_difference(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100176 newset = self.copy()
177 newset.symmetric_difference_update(other)
178 return newset
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000179 __xor__ = symmetric_difference
180
181 def symmetric_difference_update(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100182 self.__ixor__(other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000183 def __ixor__(self, other):
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000184 if self._pending_removals:
185 self._commit_removals()
Georg Brandl9dba5d92008-05-18 16:27:29 +0000186 if self is other:
187 self.data.clear()
188 else:
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100189 self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000190 return self
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000191
192 def union(self, other):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100193 return self.__class__(e for s in (self, other) for e in s)
Raymond Hettinger93fa6082008-02-05 00:20:01 +0000194 __or__ = union
Georg Brandl9dba5d92008-05-18 16:27:29 +0000195
196 def isdisjoint(self, other):
197 return len(self.intersection(other)) == 0
Batuhan Taşkaya5ae1c842019-05-20 20:01:07 +0300198
199 def __repr__(self):
200 return repr(self.data)
Ethan Smith8ef87502020-04-13 21:54:40 -0700201
202 __class_getitem__ = classmethod(GenericAlias)