]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | # Access WeakSet through the weakref module.\r |
2 | # This code is separated-out because it is needed\r | |
3 | # by abc.py to load everything else at startup.\r | |
4 | \r | |
5 | from _weakref import ref\r | |
6 | \r | |
7 | __all__ = ['WeakSet']\r | |
8 | \r | |
9 | \r | |
10 | class _IterationGuard(object):\r | |
11 | # This context manager registers itself in the current iterators of the\r | |
12 | # weak container, such as to delay all removals until the context manager\r | |
13 | # exits.\r | |
14 | # This technique should be relatively thread-safe (since sets are).\r | |
15 | \r | |
16 | def __init__(self, weakcontainer):\r | |
17 | # Don't create cycles\r | |
18 | self.weakcontainer = ref(weakcontainer)\r | |
19 | \r | |
20 | def __enter__(self):\r | |
21 | w = self.weakcontainer()\r | |
22 | if w is not None:\r | |
23 | w._iterating.add(self)\r | |
24 | return self\r | |
25 | \r | |
26 | def __exit__(self, e, t, b):\r | |
27 | w = self.weakcontainer()\r | |
28 | if w is not None:\r | |
29 | s = w._iterating\r | |
30 | s.remove(self)\r | |
31 | if not s:\r | |
32 | w._commit_removals()\r | |
33 | \r | |
34 | \r | |
35 | class WeakSet(object):\r | |
36 | def __init__(self, data=None):\r | |
37 | self.data = set()\r | |
38 | def _remove(item, selfref=ref(self)):\r | |
39 | self = selfref()\r | |
40 | if self is not None:\r | |
41 | if self._iterating:\r | |
42 | self._pending_removals.append(item)\r | |
43 | else:\r | |
44 | self.data.discard(item)\r | |
45 | self._remove = _remove\r | |
46 | # A list of keys to be removed\r | |
47 | self._pending_removals = []\r | |
48 | self._iterating = set()\r | |
49 | if data is not None:\r | |
50 | self.update(data)\r | |
51 | \r | |
52 | def _commit_removals(self):\r | |
53 | l = self._pending_removals\r | |
54 | discard = self.data.discard\r | |
55 | while l:\r | |
56 | discard(l.pop())\r | |
57 | \r | |
58 | def __iter__(self):\r | |
59 | with _IterationGuard(self):\r | |
60 | for itemref in self.data:\r | |
61 | item = itemref()\r | |
62 | if item is not None:\r | |
63 | yield item\r | |
64 | \r | |
65 | def __len__(self):\r | |
66 | return sum(x() is not None for x in self.data)\r | |
67 | \r | |
68 | def __contains__(self, item):\r | |
69 | try:\r | |
70 | wr = ref(item)\r | |
71 | except TypeError:\r | |
72 | return False\r | |
73 | return wr in self.data\r | |
74 | \r | |
75 | def __reduce__(self):\r | |
76 | return (self.__class__, (list(self),),\r | |
77 | getattr(self, '__dict__', None))\r | |
78 | \r | |
79 | __hash__ = None\r | |
80 | \r | |
81 | def add(self, item):\r | |
82 | if self._pending_removals:\r | |
83 | self._commit_removals()\r | |
84 | self.data.add(ref(item, self._remove))\r | |
85 | \r | |
86 | def clear(self):\r | |
87 | if self._pending_removals:\r | |
88 | self._commit_removals()\r | |
89 | self.data.clear()\r | |
90 | \r | |
91 | def copy(self):\r | |
92 | return self.__class__(self)\r | |
93 | \r | |
94 | def pop(self):\r | |
95 | if self._pending_removals:\r | |
96 | self._commit_removals()\r | |
97 | while True:\r | |
98 | try:\r | |
99 | itemref = self.data.pop()\r | |
100 | except KeyError:\r | |
101 | raise KeyError('pop from empty WeakSet')\r | |
102 | item = itemref()\r | |
103 | if item is not None:\r | |
104 | return item\r | |
105 | \r | |
106 | def remove(self, item):\r | |
107 | if self._pending_removals:\r | |
108 | self._commit_removals()\r | |
109 | self.data.remove(ref(item))\r | |
110 | \r | |
111 | def discard(self, item):\r | |
112 | if self._pending_removals:\r | |
113 | self._commit_removals()\r | |
114 | self.data.discard(ref(item))\r | |
115 | \r | |
116 | def update(self, other):\r | |
117 | if self._pending_removals:\r | |
118 | self._commit_removals()\r | |
119 | if isinstance(other, self.__class__):\r | |
120 | self.data.update(other.data)\r | |
121 | else:\r | |
122 | for element in other:\r | |
123 | self.add(element)\r | |
124 | \r | |
125 | def __ior__(self, other):\r | |
126 | self.update(other)\r | |
127 | return self\r | |
128 | \r | |
129 | # Helper functions for simple delegating methods.\r | |
130 | def _apply(self, other, method):\r | |
131 | if not isinstance(other, self.__class__):\r | |
132 | other = self.__class__(other)\r | |
133 | newdata = method(other.data)\r | |
134 | newset = self.__class__()\r | |
135 | newset.data = newdata\r | |
136 | return newset\r | |
137 | \r | |
138 | def difference(self, other):\r | |
139 | return self._apply(other, self.data.difference)\r | |
140 | __sub__ = difference\r | |
141 | \r | |
142 | def difference_update(self, other):\r | |
143 | if self._pending_removals:\r | |
144 | self._commit_removals()\r | |
145 | if self is other:\r | |
146 | self.data.clear()\r | |
147 | else:\r | |
148 | self.data.difference_update(ref(item) for item in other)\r | |
149 | def __isub__(self, other):\r | |
150 | if self._pending_removals:\r | |
151 | self._commit_removals()\r | |
152 | if self is other:\r | |
153 | self.data.clear()\r | |
154 | else:\r | |
155 | self.data.difference_update(ref(item) for item in other)\r | |
156 | return self\r | |
157 | \r | |
158 | def intersection(self, other):\r | |
159 | return self._apply(other, self.data.intersection)\r | |
160 | __and__ = intersection\r | |
161 | \r | |
162 | def intersection_update(self, other):\r | |
163 | if self._pending_removals:\r | |
164 | self._commit_removals()\r | |
165 | self.data.intersection_update(ref(item) for item in other)\r | |
166 | def __iand__(self, other):\r | |
167 | if self._pending_removals:\r | |
168 | self._commit_removals()\r | |
169 | self.data.intersection_update(ref(item) for item in other)\r | |
170 | return self\r | |
171 | \r | |
172 | def issubset(self, other):\r | |
173 | return self.data.issubset(ref(item) for item in other)\r | |
174 | __lt__ = issubset\r | |
175 | \r | |
176 | def __le__(self, other):\r | |
177 | return self.data <= set(ref(item) for item in other)\r | |
178 | \r | |
179 | def issuperset(self, other):\r | |
180 | return self.data.issuperset(ref(item) for item in other)\r | |
181 | __gt__ = issuperset\r | |
182 | \r | |
183 | def __ge__(self, other):\r | |
184 | return self.data >= set(ref(item) for item in other)\r | |
185 | \r | |
186 | def __eq__(self, other):\r | |
187 | if not isinstance(other, self.__class__):\r | |
188 | return NotImplemented\r | |
189 | return self.data == set(ref(item) for item in other)\r | |
190 | \r | |
191 | def symmetric_difference(self, other):\r | |
192 | return self._apply(other, self.data.symmetric_difference)\r | |
193 | __xor__ = symmetric_difference\r | |
194 | \r | |
195 | def symmetric_difference_update(self, other):\r | |
196 | if self._pending_removals:\r | |
197 | self._commit_removals()\r | |
198 | if self is other:\r | |
199 | self.data.clear()\r | |
200 | else:\r | |
201 | self.data.symmetric_difference_update(ref(item) for item in other)\r | |
202 | def __ixor__(self, other):\r | |
203 | if self._pending_removals:\r | |
204 | self._commit_removals()\r | |
205 | if self is other:\r | |
206 | self.data.clear()\r | |
207 | else:\r | |
208 | self.data.symmetric_difference_update(ref(item) for item in other)\r | |
209 | return self\r | |
210 | \r | |
211 | def union(self, other):\r | |
212 | return self._apply(other, self.data.union)\r | |
213 | __or__ = union\r | |
214 | \r | |
215 | def isdisjoint(self, other):\r | |
216 | return len(self.intersection(other)) == 0\r |