blob: 35db7a6232627a39b889491d5abd66b68d9c528e [file] [log] [blame]
Georg Brandl9dba5d92008-05-18 16:27:29 +00001import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +00002from test import support
Georg Brandl9dba5d92008-05-18 16:27:29 +00003from weakref import proxy, ref, WeakSet
4import operator
5import copy
6import string
7import os
8from random import randrange, shuffle
9import sys
10import warnings
11import collections
12from collections import UserString as ustr
Antoine Pitrouc1baa602010-01-08 17:54:23 +000013import gc
14import contextlib
Georg Brandl9dba5d92008-05-18 16:27:29 +000015
16
17class Foo:
18 pass
19
Antoine Pitroubbe2f602012-03-01 16:26:35 +010020class RefCycle:
21 def __init__(self):
22 self.cycle = self
23
Georg Brandl9dba5d92008-05-18 16:27:29 +000024
25class TestWeakSet(unittest.TestCase):
26
27 def setUp(self):
28 # need to keep references to them
29 self.items = [ustr(c) for c in ('a', 'b', 'c')]
30 self.items2 = [ustr(c) for c in ('x', 'y', 'z')]
31 self.letters = [ustr(c) for c in string.ascii_letters]
32 self.s = WeakSet(self.items)
33 self.d = dict.fromkeys(self.items)
34 self.obj = ustr('F')
35 self.fs = WeakSet([self.obj])
36
37 def test_methods(self):
38 weaksetmethods = dir(WeakSet)
39 for method in dir(set):
Georg Brandl02c0bbb2008-05-18 21:04:46 +000040 if method == 'test_c_api' or method.startswith('_'):
Georg Brandl9dba5d92008-05-18 16:27:29 +000041 continue
Ezio Melottib58e0bd2010-01-23 15:40:09 +000042 self.assertIn(method, weaksetmethods,
Georg Brandl19219702008-05-18 17:10:40 +000043 "WeakSet missing method " + method)
Georg Brandl9dba5d92008-05-18 16:27:29 +000044
45 def test_new_or_init(self):
46 self.assertRaises(TypeError, WeakSet, [], 2)
47
48 def test_len(self):
49 self.assertEqual(len(self.s), len(self.d))
50 self.assertEqual(len(self.fs), 1)
51 del self.obj
52 self.assertEqual(len(self.fs), 0)
53
54 def test_contains(self):
55 for c in self.letters:
56 self.assertEqual(c in self.s, c in self.d)
Georg Brandlf8de3fe2010-12-03 07:55:44 +000057 # 1 is not weakref'able, but that TypeError is caught by __contains__
58 self.assertNotIn(1, self.s)
Benjamin Peterson577473f2010-01-19 00:09:57 +000059 self.assertIn(self.obj, self.fs)
Georg Brandl9dba5d92008-05-18 16:27:29 +000060 del self.obj
Benjamin Peterson577473f2010-01-19 00:09:57 +000061 self.assertNotIn(ustr('F'), self.fs)
Georg Brandl9dba5d92008-05-18 16:27:29 +000062
63 def test_union(self):
64 u = self.s.union(self.items2)
65 for c in self.letters:
66 self.assertEqual(c in u, c in self.d or c in self.items2)
67 self.assertEqual(self.s, WeakSet(self.items))
68 self.assertEqual(type(u), WeakSet)
69 self.assertRaises(TypeError, self.s.union, [[]])
70 for C in set, frozenset, dict.fromkeys, list, tuple:
71 x = WeakSet(self.items + self.items2)
72 c = C(self.items2)
73 self.assertEqual(self.s.union(c), x)
74
75 def test_or(self):
76 i = self.s.union(self.items2)
77 self.assertEqual(self.s | set(self.items2), i)
78 self.assertEqual(self.s | frozenset(self.items2), i)
79
80 def test_intersection(self):
81 i = self.s.intersection(self.items2)
82 for c in self.letters:
83 self.assertEqual(c in i, c in self.d and c in self.items2)
84 self.assertEqual(self.s, WeakSet(self.items))
85 self.assertEqual(type(i), WeakSet)
86 for C in set, frozenset, dict.fromkeys, list, tuple:
87 x = WeakSet([])
88 self.assertEqual(self.s.intersection(C(self.items2)), x)
89
90 def test_isdisjoint(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000091 self.assertTrue(self.s.isdisjoint(WeakSet(self.items2)))
92 self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters)))
Georg Brandl9dba5d92008-05-18 16:27:29 +000093
94 def test_and(self):
95 i = self.s.intersection(self.items2)
96 self.assertEqual(self.s & set(self.items2), i)
97 self.assertEqual(self.s & frozenset(self.items2), i)
98
99 def test_difference(self):
100 i = self.s.difference(self.items2)
101 for c in self.letters:
102 self.assertEqual(c in i, c in self.d and c not in self.items2)
103 self.assertEqual(self.s, WeakSet(self.items))
104 self.assertEqual(type(i), WeakSet)
105 self.assertRaises(TypeError, self.s.difference, [[]])
106
107 def test_sub(self):
108 i = self.s.difference(self.items2)
109 self.assertEqual(self.s - set(self.items2), i)
110 self.assertEqual(self.s - frozenset(self.items2), i)
111
112 def test_symmetric_difference(self):
113 i = self.s.symmetric_difference(self.items2)
114 for c in self.letters:
115 self.assertEqual(c in i, (c in self.d) ^ (c in self.items2))
116 self.assertEqual(self.s, WeakSet(self.items))
117 self.assertEqual(type(i), WeakSet)
118 self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
119
120 def test_xor(self):
121 i = self.s.symmetric_difference(self.items2)
122 self.assertEqual(self.s ^ set(self.items2), i)
123 self.assertEqual(self.s ^ frozenset(self.items2), i)
124
125 def test_sub_and_super(self):
126 pl, ql, rl = map(lambda s: [ustr(c) for c in s], ['ab', 'abcde', 'def'])
127 p, q, r = map(WeakSet, (pl, ql, rl))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000128 self.assertTrue(p < q)
129 self.assertTrue(p <= q)
130 self.assertTrue(q <= q)
131 self.assertTrue(q > p)
132 self.assertTrue(q >= p)
133 self.assertFalse(q < r)
134 self.assertFalse(q <= r)
135 self.assertFalse(q > r)
136 self.assertFalse(q >= r)
137 self.assertTrue(set('a').issubset('abc'))
138 self.assertTrue(set('abc').issuperset('a'))
139 self.assertFalse(set('a').issubset('cbs'))
140 self.assertFalse(set('cbs').issuperset('a'))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000141
142 def test_gc(self):
143 # Create a nest of cycles to exercise overall ref count check
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000144 s = WeakSet(Foo() for i in range(1000))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000145 for elem in s:
146 elem.cycle = s
147 elem.sub = elem
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000148 elem.set = WeakSet([elem])
Georg Brandl9dba5d92008-05-18 16:27:29 +0000149
150 def test_subclass_with_custom_hash(self):
151 # Bug #1257731
152 class H(WeakSet):
153 def __hash__(self):
154 return int(id(self) & 0x7fffffff)
155 s=H()
156 f=set()
157 f.add(s)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000158 self.assertIn(s, f)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000159 f.remove(s)
160 f.add(s)
161 f.discard(s)
162
163 def test_init(self):
164 s = WeakSet()
165 s.__init__(self.items)
166 self.assertEqual(s, self.s)
167 s.__init__(self.items2)
168 self.assertEqual(s, WeakSet(self.items2))
169 self.assertRaises(TypeError, s.__init__, s, 2);
170 self.assertRaises(TypeError, s.__init__, 1);
171
172 def test_constructor_identity(self):
173 s = WeakSet(self.items)
174 t = WeakSet(s)
175 self.assertNotEqual(id(s), id(t))
176
Georg Brandl9dba5d92008-05-18 16:27:29 +0000177 def test_hash(self):
178 self.assertRaises(TypeError, hash, self.s)
179
180 def test_clear(self):
181 self.s.clear()
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000182 self.assertEqual(self.s, WeakSet([]))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000183 self.assertEqual(len(self.s), 0)
184
185 def test_copy(self):
186 dup = self.s.copy()
187 self.assertEqual(self.s, dup)
188 self.assertNotEqual(id(self.s), id(dup))
189
190 def test_add(self):
191 x = ustr('Q')
192 self.s.add(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000193 self.assertIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000194 dup = self.s.copy()
195 self.s.add(x)
196 self.assertEqual(self.s, dup)
197 self.assertRaises(TypeError, self.s.add, [])
198 self.fs.add(Foo())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000199 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000200 self.fs.add(self.obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000201 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000202
203 def test_remove(self):
204 x = ustr('a')
205 self.s.remove(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000206 self.assertNotIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000207 self.assertRaises(KeyError, self.s.remove, x)
208 self.assertRaises(TypeError, self.s.remove, [])
209
210 def test_discard(self):
211 a, q = ustr('a'), ustr('Q')
212 self.s.discard(a)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000213 self.assertNotIn(a, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000214 self.s.discard(q)
215 self.assertRaises(TypeError, self.s.discard, [])
216
217 def test_pop(self):
218 for i in range(len(self.s)):
219 elem = self.s.pop()
Benjamin Peterson577473f2010-01-19 00:09:57 +0000220 self.assertNotIn(elem, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000221 self.assertRaises(KeyError, self.s.pop)
222
223 def test_update(self):
224 retval = self.s.update(self.items2)
225 self.assertEqual(retval, None)
226 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000227 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000228 self.assertRaises(TypeError, self.s.update, [[]])
229
230 def test_update_set(self):
231 self.s.update(set(self.items2))
232 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000233 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000234
235 def test_ior(self):
236 self.s |= set(self.items2)
237 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000238 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000239
240 def test_intersection_update(self):
241 retval = self.s.intersection_update(self.items2)
242 self.assertEqual(retval, None)
243 for c in (self.items + self.items2):
244 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000245 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000246 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000247 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000248 self.assertRaises(TypeError, self.s.intersection_update, [[]])
249
250 def test_iand(self):
251 self.s &= set(self.items2)
252 for c in (self.items + self.items2):
253 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000254 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000255 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000256 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000257
258 def test_difference_update(self):
259 retval = self.s.difference_update(self.items2)
260 self.assertEqual(retval, None)
261 for c in (self.items + self.items2):
262 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000263 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000264 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000265 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000266 self.assertRaises(TypeError, self.s.difference_update, [[]])
267 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
268
269 def test_isub(self):
270 self.s -= set(self.items2)
271 for c in (self.items + self.items2):
272 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000273 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000274 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000275 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000276
277 def test_symmetric_difference_update(self):
278 retval = self.s.symmetric_difference_update(self.items2)
279 self.assertEqual(retval, None)
280 for c in (self.items + self.items2):
281 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000282 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000283 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000284 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000285 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
286
287 def test_ixor(self):
288 self.s ^= set(self.items2)
289 for c in (self.items + self.items2):
290 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000291 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000292 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000293 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000294
295 def test_inplace_on_self(self):
296 t = self.s.copy()
297 t |= t
298 self.assertEqual(t, self.s)
299 t &= t
300 self.assertEqual(t, self.s)
301 t -= t
302 self.assertEqual(t, WeakSet())
303 t = self.s.copy()
304 t ^= t
305 self.assertEqual(t, WeakSet())
306
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000307 def test_eq(self):
308 # issue 5964
309 self.assertTrue(self.s == self.s)
310 self.assertTrue(self.s == WeakSet(self.items))
311 self.assertFalse(self.s == set(self.items))
312 self.assertFalse(self.s == list(self.items))
313 self.assertFalse(self.s == tuple(self.items))
314 self.assertFalse(self.s == WeakSet([Foo]))
315 self.assertFalse(self.s == 1)
316
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000317 def test_weak_destroy_while_iterating(self):
318 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
319 # Create new items to be sure no-one else holds a reference
320 items = [ustr(c) for c in ('a', 'b', 'c')]
321 s = WeakSet(items)
322 it = iter(s)
323 next(it) # Trigger internal iteration
324 # Destroy an item
325 del items[-1]
326 gc.collect() # just in case
327 # We have removed either the first consumed items, or another one
328 self.assertIn(len(list(it)), [len(items), len(items) - 1])
329 del it
330 # The removal has been committed
331 self.assertEqual(len(s), len(items))
332
333 def test_weak_destroy_and_mutate_while_iterating(self):
334 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
335 items = [ustr(c) for c in string.ascii_letters]
336 s = WeakSet(items)
337 @contextlib.contextmanager
338 def testcontext():
339 try:
340 it = iter(s)
341 next(it)
Antoine Pitrou95a97b72012-02-21 00:00:06 +0100342 del it
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000343 # Schedule an item for removal and recreate it
344 u = ustr(str(items.pop()))
345 gc.collect() # just in case
346 yield u
347 finally:
348 it = None # should commit all removals
349
350 with testcontext() as u:
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000351 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000352 with testcontext() as u:
353 self.assertRaises(KeyError, s.remove, u)
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000354 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000355 with testcontext() as u:
356 s.add(u)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000357 self.assertIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000358 t = s.copy()
359 with testcontext() as u:
360 s.update(t)
361 self.assertEqual(len(s), len(t))
362 with testcontext() as u:
363 s.clear()
364 self.assertEqual(len(s), 0)
365
Antoine Pitroubbe2f602012-03-01 16:26:35 +0100366 def test_len_cycles(self):
367 N = 20
368 items = [RefCycle() for i in range(N)]
369 s = WeakSet(items)
370 del items
371 it = iter(s)
372 try:
373 next(it)
374 except StopIteration:
375 pass
376 gc.collect()
377 n1 = len(s)
378 del it
379 gc.collect()
380 n2 = len(s)
381 # one item may be kept alive inside the iterator
382 self.assertIn(n1, (0, 1))
383 self.assertEqual(n2, 0)
384
385 def test_len_race(self):
386 # Extended sanity checks for len() in the face of cyclic collection
387 self.addCleanup(gc.set_threshold, *gc.get_threshold())
388 for th in range(1, 100):
389 N = 20
390 gc.collect(0)
391 gc.set_threshold(th, th, th)
392 items = [RefCycle() for i in range(N)]
393 s = WeakSet(items)
394 del items
395 # All items will be collected at next garbage collection pass
396 it = iter(s)
397 try:
398 next(it)
399 except StopIteration:
400 pass
401 n1 = len(s)
402 del it
403 n2 = len(s)
404 self.assertGreaterEqual(n1, 0)
405 self.assertLessEqual(n1, N)
406 self.assertGreaterEqual(n2, 0)
407 self.assertLessEqual(n2, n1)
408
Georg Brandl9dba5d92008-05-18 16:27:29 +0000409
410def test_main(verbose=None):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000411 support.run_unittest(TestWeakSet)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000412
413if __name__ == "__main__":
414 test_main(verbose=True)