blob: fb22879dfab92de7c301a73423a4319ea4b96904 [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')]
Meador Inge653f9322012-03-04 22:15:38 -060031 self.ab_items = [ustr(c) for c in 'ab']
32 self.abcde_items = [ustr(c) for c in 'abcde']
33 self.def_items = [ustr(c) for c in 'def']
34 self.ab_weakset = WeakSet(self.ab_items)
35 self.abcde_weakset = WeakSet(self.abcde_items)
36 self.def_weakset = WeakSet(self.def_items)
Georg Brandl9dba5d92008-05-18 16:27:29 +000037 self.letters = [ustr(c) for c in string.ascii_letters]
38 self.s = WeakSet(self.items)
39 self.d = dict.fromkeys(self.items)
40 self.obj = ustr('F')
41 self.fs = WeakSet([self.obj])
42
43 def test_methods(self):
44 weaksetmethods = dir(WeakSet)
45 for method in dir(set):
Georg Brandl02c0bbb2008-05-18 21:04:46 +000046 if method == 'test_c_api' or method.startswith('_'):
Georg Brandl9dba5d92008-05-18 16:27:29 +000047 continue
Ezio Melottib58e0bd2010-01-23 15:40:09 +000048 self.assertIn(method, weaksetmethods,
Georg Brandl19219702008-05-18 17:10:40 +000049 "WeakSet missing method " + method)
Georg Brandl9dba5d92008-05-18 16:27:29 +000050
51 def test_new_or_init(self):
52 self.assertRaises(TypeError, WeakSet, [], 2)
53
54 def test_len(self):
55 self.assertEqual(len(self.s), len(self.d))
56 self.assertEqual(len(self.fs), 1)
57 del self.obj
58 self.assertEqual(len(self.fs), 0)
59
60 def test_contains(self):
61 for c in self.letters:
62 self.assertEqual(c in self.s, c in self.d)
Georg Brandlf8de3fe2010-12-03 07:55:44 +000063 # 1 is not weakref'able, but that TypeError is caught by __contains__
64 self.assertNotIn(1, self.s)
Benjamin Peterson577473f2010-01-19 00:09:57 +000065 self.assertIn(self.obj, self.fs)
Georg Brandl9dba5d92008-05-18 16:27:29 +000066 del self.obj
Benjamin Peterson577473f2010-01-19 00:09:57 +000067 self.assertNotIn(ustr('F'), self.fs)
Georg Brandl9dba5d92008-05-18 16:27:29 +000068
69 def test_union(self):
70 u = self.s.union(self.items2)
71 for c in self.letters:
72 self.assertEqual(c in u, c in self.d or c in self.items2)
73 self.assertEqual(self.s, WeakSet(self.items))
74 self.assertEqual(type(u), WeakSet)
75 self.assertRaises(TypeError, self.s.union, [[]])
76 for C in set, frozenset, dict.fromkeys, list, tuple:
77 x = WeakSet(self.items + self.items2)
78 c = C(self.items2)
79 self.assertEqual(self.s.union(c), x)
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010080 del c
81 self.assertEqual(len(u), len(self.items) + len(self.items2))
82 self.items2.pop()
83 gc.collect()
84 self.assertEqual(len(u), len(self.items) + len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +000085
86 def test_or(self):
87 i = self.s.union(self.items2)
88 self.assertEqual(self.s | set(self.items2), i)
89 self.assertEqual(self.s | frozenset(self.items2), i)
90
91 def test_intersection(self):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010092 s = WeakSet(self.letters)
93 i = s.intersection(self.items2)
Georg Brandl9dba5d92008-05-18 16:27:29 +000094 for c in self.letters:
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010095 self.assertEqual(c in i, c in self.items2 and c in self.letters)
96 self.assertEqual(s, WeakSet(self.letters))
Georg Brandl9dba5d92008-05-18 16:27:29 +000097 self.assertEqual(type(i), WeakSet)
98 for C in set, frozenset, dict.fromkeys, list, tuple:
99 x = WeakSet([])
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100100 self.assertEqual(i.intersection(C(self.items)), x)
101 self.assertEqual(len(i), len(self.items2))
102 self.items2.pop()
103 gc.collect()
104 self.assertEqual(len(i), len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000105
106 def test_isdisjoint(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000107 self.assertTrue(self.s.isdisjoint(WeakSet(self.items2)))
108 self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters)))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000109
110 def test_and(self):
111 i = self.s.intersection(self.items2)
112 self.assertEqual(self.s & set(self.items2), i)
113 self.assertEqual(self.s & frozenset(self.items2), i)
114
115 def test_difference(self):
116 i = self.s.difference(self.items2)
117 for c in self.letters:
118 self.assertEqual(c in i, c in self.d and c not in self.items2)
119 self.assertEqual(self.s, WeakSet(self.items))
120 self.assertEqual(type(i), WeakSet)
121 self.assertRaises(TypeError, self.s.difference, [[]])
122
123 def test_sub(self):
124 i = self.s.difference(self.items2)
125 self.assertEqual(self.s - set(self.items2), i)
126 self.assertEqual(self.s - frozenset(self.items2), i)
127
128 def test_symmetric_difference(self):
129 i = self.s.symmetric_difference(self.items2)
130 for c in self.letters:
131 self.assertEqual(c in i, (c in self.d) ^ (c in self.items2))
132 self.assertEqual(self.s, WeakSet(self.items))
133 self.assertEqual(type(i), WeakSet)
134 self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100135 self.assertEqual(len(i), len(self.items) + len(self.items2))
136 self.items2.pop()
137 gc.collect()
138 self.assertEqual(len(i), len(self.items) + len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000139
140 def test_xor(self):
141 i = self.s.symmetric_difference(self.items2)
142 self.assertEqual(self.s ^ set(self.items2), i)
143 self.assertEqual(self.s ^ frozenset(self.items2), i)
144
145 def test_sub_and_super(self):
Meador Inge653f9322012-03-04 22:15:38 -0600146 self.assertTrue(self.ab_weakset <= self.abcde_weakset)
147 self.assertTrue(self.abcde_weakset <= self.abcde_weakset)
148 self.assertTrue(self.abcde_weakset >= self.ab_weakset)
149 self.assertFalse(self.abcde_weakset <= self.def_weakset)
150 self.assertFalse(self.abcde_weakset >= self.def_weakset)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000151 self.assertTrue(set('a').issubset('abc'))
152 self.assertTrue(set('abc').issuperset('a'))
153 self.assertFalse(set('a').issubset('cbs'))
154 self.assertFalse(set('cbs').issuperset('a'))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000155
Meador Inge653f9322012-03-04 22:15:38 -0600156 def test_lt(self):
157 self.assertTrue(self.ab_weakset < self.abcde_weakset)
158 self.assertFalse(self.abcde_weakset < self.def_weakset)
159 self.assertFalse(self.ab_weakset < self.ab_weakset)
160 self.assertFalse(WeakSet() < WeakSet())
161
162 def test_gt(self):
163 self.assertTrue(self.abcde_weakset > self.ab_weakset)
164 self.assertFalse(self.abcde_weakset > self.def_weakset)
165 self.assertFalse(self.ab_weakset > self.ab_weakset)
166 self.assertFalse(WeakSet() > WeakSet())
167
Georg Brandl9dba5d92008-05-18 16:27:29 +0000168 def test_gc(self):
169 # Create a nest of cycles to exercise overall ref count check
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000170 s = WeakSet(Foo() for i in range(1000))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000171 for elem in s:
172 elem.cycle = s
173 elem.sub = elem
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000174 elem.set = WeakSet([elem])
Georg Brandl9dba5d92008-05-18 16:27:29 +0000175
176 def test_subclass_with_custom_hash(self):
177 # Bug #1257731
178 class H(WeakSet):
179 def __hash__(self):
180 return int(id(self) & 0x7fffffff)
181 s=H()
182 f=set()
183 f.add(s)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000184 self.assertIn(s, f)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000185 f.remove(s)
186 f.add(s)
187 f.discard(s)
188
189 def test_init(self):
190 s = WeakSet()
191 s.__init__(self.items)
192 self.assertEqual(s, self.s)
193 s.__init__(self.items2)
194 self.assertEqual(s, WeakSet(self.items2))
195 self.assertRaises(TypeError, s.__init__, s, 2);
196 self.assertRaises(TypeError, s.__init__, 1);
197
198 def test_constructor_identity(self):
199 s = WeakSet(self.items)
200 t = WeakSet(s)
201 self.assertNotEqual(id(s), id(t))
202
Georg Brandl9dba5d92008-05-18 16:27:29 +0000203 def test_hash(self):
204 self.assertRaises(TypeError, hash, self.s)
205
206 def test_clear(self):
207 self.s.clear()
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000208 self.assertEqual(self.s, WeakSet([]))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000209 self.assertEqual(len(self.s), 0)
210
211 def test_copy(self):
212 dup = self.s.copy()
213 self.assertEqual(self.s, dup)
214 self.assertNotEqual(id(self.s), id(dup))
215
216 def test_add(self):
217 x = ustr('Q')
218 self.s.add(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000219 self.assertIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000220 dup = self.s.copy()
221 self.s.add(x)
222 self.assertEqual(self.s, dup)
223 self.assertRaises(TypeError, self.s.add, [])
224 self.fs.add(Foo())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000225 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000226 self.fs.add(self.obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000227 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000228
229 def test_remove(self):
230 x = ustr('a')
231 self.s.remove(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000232 self.assertNotIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000233 self.assertRaises(KeyError, self.s.remove, x)
234 self.assertRaises(TypeError, self.s.remove, [])
235
236 def test_discard(self):
237 a, q = ustr('a'), ustr('Q')
238 self.s.discard(a)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000239 self.assertNotIn(a, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000240 self.s.discard(q)
241 self.assertRaises(TypeError, self.s.discard, [])
242
243 def test_pop(self):
244 for i in range(len(self.s)):
245 elem = self.s.pop()
Benjamin Peterson577473f2010-01-19 00:09:57 +0000246 self.assertNotIn(elem, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000247 self.assertRaises(KeyError, self.s.pop)
248
249 def test_update(self):
250 retval = self.s.update(self.items2)
251 self.assertEqual(retval, None)
252 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000253 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000254 self.assertRaises(TypeError, self.s.update, [[]])
255
256 def test_update_set(self):
257 self.s.update(set(self.items2))
258 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000259 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000260
261 def test_ior(self):
262 self.s |= set(self.items2)
263 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000264 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000265
266 def test_intersection_update(self):
267 retval = self.s.intersection_update(self.items2)
268 self.assertEqual(retval, None)
269 for c in (self.items + self.items2):
270 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000271 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000272 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000273 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000274 self.assertRaises(TypeError, self.s.intersection_update, [[]])
275
276 def test_iand(self):
277 self.s &= set(self.items2)
278 for c in (self.items + self.items2):
279 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000280 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000281 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000282 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000283
284 def test_difference_update(self):
285 retval = self.s.difference_update(self.items2)
286 self.assertEqual(retval, None)
287 for c in (self.items + self.items2):
288 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000289 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000290 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000291 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000292 self.assertRaises(TypeError, self.s.difference_update, [[]])
293 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
294
295 def test_isub(self):
296 self.s -= set(self.items2)
297 for c in (self.items + self.items2):
298 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000299 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000300 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000301 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000302
303 def test_symmetric_difference_update(self):
304 retval = self.s.symmetric_difference_update(self.items2)
305 self.assertEqual(retval, None)
306 for c in (self.items + self.items2):
307 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000308 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000309 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000310 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000311 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
312
313 def test_ixor(self):
314 self.s ^= set(self.items2)
315 for c in (self.items + self.items2):
316 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000317 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000318 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000319 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000320
321 def test_inplace_on_self(self):
322 t = self.s.copy()
323 t |= t
324 self.assertEqual(t, self.s)
325 t &= t
326 self.assertEqual(t, self.s)
327 t -= t
328 self.assertEqual(t, WeakSet())
329 t = self.s.copy()
330 t ^= t
331 self.assertEqual(t, WeakSet())
332
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000333 def test_eq(self):
334 # issue 5964
335 self.assertTrue(self.s == self.s)
336 self.assertTrue(self.s == WeakSet(self.items))
337 self.assertFalse(self.s == set(self.items))
338 self.assertFalse(self.s == list(self.items))
339 self.assertFalse(self.s == tuple(self.items))
340 self.assertFalse(self.s == WeakSet([Foo]))
341 self.assertFalse(self.s == 1)
342
Benjamin Peterson67246122013-05-22 13:27:25 -0700343 def test_ne(self):
344 self.assertTrue(self.s != set(self.items))
345 s1 = WeakSet()
346 s2 = WeakSet()
347 self.assertFalse(s1 != s2)
348
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000349 def test_weak_destroy_while_iterating(self):
350 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
351 # Create new items to be sure no-one else holds a reference
352 items = [ustr(c) for c in ('a', 'b', 'c')]
353 s = WeakSet(items)
354 it = iter(s)
355 next(it) # Trigger internal iteration
356 # Destroy an item
357 del items[-1]
358 gc.collect() # just in case
359 # We have removed either the first consumed items, or another one
360 self.assertIn(len(list(it)), [len(items), len(items) - 1])
361 del it
362 # The removal has been committed
363 self.assertEqual(len(s), len(items))
364
365 def test_weak_destroy_and_mutate_while_iterating(self):
366 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
367 items = [ustr(c) for c in string.ascii_letters]
368 s = WeakSet(items)
369 @contextlib.contextmanager
370 def testcontext():
371 try:
372 it = iter(s)
Antoine Pitrou320b3912013-12-18 00:28:36 +0100373 # Start iterator
374 yielded = ustr(str(next(it)))
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000375 # Schedule an item for removal and recreate it
376 u = ustr(str(items.pop()))
Antoine Pitrou320b3912013-12-18 00:28:36 +0100377 if yielded == u:
378 # The iterator still has a reference to the removed item,
379 # advance it (issue #20006).
380 next(it)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000381 gc.collect() # just in case
382 yield u
383 finally:
384 it = None # should commit all removals
385
386 with testcontext() as u:
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000387 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000388 with testcontext() as u:
389 self.assertRaises(KeyError, s.remove, u)
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000390 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000391 with testcontext() as u:
392 s.add(u)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000393 self.assertIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000394 t = s.copy()
395 with testcontext() as u:
396 s.update(t)
397 self.assertEqual(len(s), len(t))
398 with testcontext() as u:
399 s.clear()
400 self.assertEqual(len(s), 0)
401
Antoine Pitroubbe2f602012-03-01 16:26:35 +0100402 def test_len_cycles(self):
403 N = 20
404 items = [RefCycle() for i in range(N)]
405 s = WeakSet(items)
406 del items
407 it = iter(s)
408 try:
409 next(it)
410 except StopIteration:
411 pass
412 gc.collect()
413 n1 = len(s)
414 del it
415 gc.collect()
416 n2 = len(s)
417 # one item may be kept alive inside the iterator
418 self.assertIn(n1, (0, 1))
419 self.assertEqual(n2, 0)
420
421 def test_len_race(self):
422 # Extended sanity checks for len() in the face of cyclic collection
423 self.addCleanup(gc.set_threshold, *gc.get_threshold())
424 for th in range(1, 100):
425 N = 20
426 gc.collect(0)
427 gc.set_threshold(th, th, th)
428 items = [RefCycle() for i in range(N)]
429 s = WeakSet(items)
430 del items
431 # All items will be collected at next garbage collection pass
432 it = iter(s)
433 try:
434 next(it)
435 except StopIteration:
436 pass
437 n1 = len(s)
438 del it
439 n2 = len(s)
440 self.assertGreaterEqual(n1, 0)
441 self.assertLessEqual(n1, N)
442 self.assertGreaterEqual(n2, 0)
443 self.assertLessEqual(n2, n1)
444
Georg Brandl9dba5d92008-05-18 16:27:29 +0000445
446def test_main(verbose=None):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000447 support.run_unittest(TestWeakSet)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000448
449if __name__ == "__main__":
450 test_main(verbose=True)