blob: 9ce672b8158f0a5611a32639a96aac33913d399e [file] [log] [blame]
Georg Brandl9dba5d92008-05-18 16:27:29 +00001import unittest
Georg Brandl9dba5d92008-05-18 16:27:29 +00002from weakref import proxy, ref, WeakSet
3import operator
4import copy
5import string
6import os
7from random import randrange, shuffle
8import sys
9import warnings
10import collections
11from collections import UserString as ustr
Antoine Pitrouc1baa602010-01-08 17:54:23 +000012import gc
13import contextlib
Georg Brandl9dba5d92008-05-18 16:27:29 +000014
15
16class Foo:
17 pass
18
Antoine Pitroubbe2f602012-03-01 16:26:35 +010019class RefCycle:
20 def __init__(self):
21 self.cycle = self
22
Georg Brandl9dba5d92008-05-18 16:27:29 +000023
24class TestWeakSet(unittest.TestCase):
25
26 def setUp(self):
27 # need to keep references to them
28 self.items = [ustr(c) for c in ('a', 'b', 'c')]
29 self.items2 = [ustr(c) for c in ('x', 'y', 'z')]
Meador Inge653f9322012-03-04 22:15:38 -060030 self.ab_items = [ustr(c) for c in 'ab']
31 self.abcde_items = [ustr(c) for c in 'abcde']
32 self.def_items = [ustr(c) for c in 'def']
33 self.ab_weakset = WeakSet(self.ab_items)
34 self.abcde_weakset = WeakSet(self.abcde_items)
35 self.def_weakset = WeakSet(self.def_items)
Georg Brandl9dba5d92008-05-18 16:27:29 +000036 self.letters = [ustr(c) for c in string.ascii_letters]
37 self.s = WeakSet(self.items)
38 self.d = dict.fromkeys(self.items)
39 self.obj = ustr('F')
40 self.fs = WeakSet([self.obj])
41
42 def test_methods(self):
43 weaksetmethods = dir(WeakSet)
44 for method in dir(set):
Georg Brandl02c0bbb2008-05-18 21:04:46 +000045 if method == 'test_c_api' or method.startswith('_'):
Georg Brandl9dba5d92008-05-18 16:27:29 +000046 continue
Ezio Melottib58e0bd2010-01-23 15:40:09 +000047 self.assertIn(method, weaksetmethods,
Georg Brandl19219702008-05-18 17:10:40 +000048 "WeakSet missing method " + method)
Georg Brandl9dba5d92008-05-18 16:27:29 +000049
50 def test_new_or_init(self):
51 self.assertRaises(TypeError, WeakSet, [], 2)
52
53 def test_len(self):
54 self.assertEqual(len(self.s), len(self.d))
55 self.assertEqual(len(self.fs), 1)
56 del self.obj
57 self.assertEqual(len(self.fs), 0)
58
59 def test_contains(self):
60 for c in self.letters:
61 self.assertEqual(c in self.s, c in self.d)
Georg Brandlf8de3fe2010-12-03 07:55:44 +000062 # 1 is not weakref'able, but that TypeError is caught by __contains__
63 self.assertNotIn(1, self.s)
Benjamin Peterson577473f2010-01-19 00:09:57 +000064 self.assertIn(self.obj, self.fs)
Georg Brandl9dba5d92008-05-18 16:27:29 +000065 del self.obj
Benjamin Peterson577473f2010-01-19 00:09:57 +000066 self.assertNotIn(ustr('F'), self.fs)
Georg Brandl9dba5d92008-05-18 16:27:29 +000067
68 def test_union(self):
69 u = self.s.union(self.items2)
70 for c in self.letters:
71 self.assertEqual(c in u, c in self.d or c in self.items2)
72 self.assertEqual(self.s, WeakSet(self.items))
73 self.assertEqual(type(u), WeakSet)
74 self.assertRaises(TypeError, self.s.union, [[]])
75 for C in set, frozenset, dict.fromkeys, list, tuple:
76 x = WeakSet(self.items + self.items2)
77 c = C(self.items2)
78 self.assertEqual(self.s.union(c), x)
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010079 del c
80 self.assertEqual(len(u), len(self.items) + len(self.items2))
81 self.items2.pop()
82 gc.collect()
83 self.assertEqual(len(u), len(self.items) + len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +000084
85 def test_or(self):
86 i = self.s.union(self.items2)
87 self.assertEqual(self.s | set(self.items2), i)
88 self.assertEqual(self.s | frozenset(self.items2), i)
89
90 def test_intersection(self):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010091 s = WeakSet(self.letters)
92 i = s.intersection(self.items2)
Georg Brandl9dba5d92008-05-18 16:27:29 +000093 for c in self.letters:
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010094 self.assertEqual(c in i, c in self.items2 and c in self.letters)
95 self.assertEqual(s, WeakSet(self.letters))
Georg Brandl9dba5d92008-05-18 16:27:29 +000096 self.assertEqual(type(i), WeakSet)
97 for C in set, frozenset, dict.fromkeys, list, tuple:
98 x = WeakSet([])
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010099 self.assertEqual(i.intersection(C(self.items)), x)
100 self.assertEqual(len(i), len(self.items2))
101 self.items2.pop()
102 gc.collect()
103 self.assertEqual(len(i), len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000104
105 def test_isdisjoint(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000106 self.assertTrue(self.s.isdisjoint(WeakSet(self.items2)))
107 self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters)))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000108
109 def test_and(self):
110 i = self.s.intersection(self.items2)
111 self.assertEqual(self.s & set(self.items2), i)
112 self.assertEqual(self.s & frozenset(self.items2), i)
113
114 def test_difference(self):
115 i = self.s.difference(self.items2)
116 for c in self.letters:
117 self.assertEqual(c in i, c in self.d and c not in self.items2)
118 self.assertEqual(self.s, WeakSet(self.items))
119 self.assertEqual(type(i), WeakSet)
120 self.assertRaises(TypeError, self.s.difference, [[]])
121
122 def test_sub(self):
123 i = self.s.difference(self.items2)
124 self.assertEqual(self.s - set(self.items2), i)
125 self.assertEqual(self.s - frozenset(self.items2), i)
126
127 def test_symmetric_difference(self):
128 i = self.s.symmetric_difference(self.items2)
129 for c in self.letters:
130 self.assertEqual(c in i, (c in self.d) ^ (c in self.items2))
131 self.assertEqual(self.s, WeakSet(self.items))
132 self.assertEqual(type(i), WeakSet)
133 self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100134 self.assertEqual(len(i), len(self.items) + len(self.items2))
135 self.items2.pop()
136 gc.collect()
137 self.assertEqual(len(i), len(self.items) + len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000138
139 def test_xor(self):
140 i = self.s.symmetric_difference(self.items2)
141 self.assertEqual(self.s ^ set(self.items2), i)
142 self.assertEqual(self.s ^ frozenset(self.items2), i)
143
144 def test_sub_and_super(self):
Meador Inge653f9322012-03-04 22:15:38 -0600145 self.assertTrue(self.ab_weakset <= self.abcde_weakset)
146 self.assertTrue(self.abcde_weakset <= self.abcde_weakset)
147 self.assertTrue(self.abcde_weakset >= self.ab_weakset)
148 self.assertFalse(self.abcde_weakset <= self.def_weakset)
149 self.assertFalse(self.abcde_weakset >= self.def_weakset)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000150 self.assertTrue(set('a').issubset('abc'))
151 self.assertTrue(set('abc').issuperset('a'))
152 self.assertFalse(set('a').issubset('cbs'))
153 self.assertFalse(set('cbs').issuperset('a'))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000154
Meador Inge653f9322012-03-04 22:15:38 -0600155 def test_lt(self):
156 self.assertTrue(self.ab_weakset < self.abcde_weakset)
157 self.assertFalse(self.abcde_weakset < self.def_weakset)
158 self.assertFalse(self.ab_weakset < self.ab_weakset)
159 self.assertFalse(WeakSet() < WeakSet())
160
161 def test_gt(self):
162 self.assertTrue(self.abcde_weakset > self.ab_weakset)
163 self.assertFalse(self.abcde_weakset > self.def_weakset)
164 self.assertFalse(self.ab_weakset > self.ab_weakset)
165 self.assertFalse(WeakSet() > WeakSet())
166
Georg Brandl9dba5d92008-05-18 16:27:29 +0000167 def test_gc(self):
168 # Create a nest of cycles to exercise overall ref count check
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000169 s = WeakSet(Foo() for i in range(1000))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000170 for elem in s:
171 elem.cycle = s
172 elem.sub = elem
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000173 elem.set = WeakSet([elem])
Georg Brandl9dba5d92008-05-18 16:27:29 +0000174
175 def test_subclass_with_custom_hash(self):
176 # Bug #1257731
177 class H(WeakSet):
178 def __hash__(self):
179 return int(id(self) & 0x7fffffff)
180 s=H()
181 f=set()
182 f.add(s)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000183 self.assertIn(s, f)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000184 f.remove(s)
185 f.add(s)
186 f.discard(s)
187
188 def test_init(self):
189 s = WeakSet()
190 s.__init__(self.items)
191 self.assertEqual(s, self.s)
192 s.__init__(self.items2)
193 self.assertEqual(s, WeakSet(self.items2))
194 self.assertRaises(TypeError, s.__init__, s, 2);
195 self.assertRaises(TypeError, s.__init__, 1);
196
197 def test_constructor_identity(self):
198 s = WeakSet(self.items)
199 t = WeakSet(s)
200 self.assertNotEqual(id(s), id(t))
201
Georg Brandl9dba5d92008-05-18 16:27:29 +0000202 def test_hash(self):
203 self.assertRaises(TypeError, hash, self.s)
204
205 def test_clear(self):
206 self.s.clear()
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000207 self.assertEqual(self.s, WeakSet([]))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000208 self.assertEqual(len(self.s), 0)
209
210 def test_copy(self):
211 dup = self.s.copy()
212 self.assertEqual(self.s, dup)
213 self.assertNotEqual(id(self.s), id(dup))
214
215 def test_add(self):
216 x = ustr('Q')
217 self.s.add(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000218 self.assertIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000219 dup = self.s.copy()
220 self.s.add(x)
221 self.assertEqual(self.s, dup)
222 self.assertRaises(TypeError, self.s.add, [])
223 self.fs.add(Foo())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000224 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000225 self.fs.add(self.obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000226 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000227
228 def test_remove(self):
229 x = ustr('a')
230 self.s.remove(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000231 self.assertNotIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000232 self.assertRaises(KeyError, self.s.remove, x)
233 self.assertRaises(TypeError, self.s.remove, [])
234
235 def test_discard(self):
236 a, q = ustr('a'), ustr('Q')
237 self.s.discard(a)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000238 self.assertNotIn(a, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000239 self.s.discard(q)
240 self.assertRaises(TypeError, self.s.discard, [])
241
242 def test_pop(self):
243 for i in range(len(self.s)):
244 elem = self.s.pop()
Benjamin Peterson577473f2010-01-19 00:09:57 +0000245 self.assertNotIn(elem, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000246 self.assertRaises(KeyError, self.s.pop)
247
248 def test_update(self):
249 retval = self.s.update(self.items2)
250 self.assertEqual(retval, None)
251 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000252 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000253 self.assertRaises(TypeError, self.s.update, [[]])
254
255 def test_update_set(self):
256 self.s.update(set(self.items2))
257 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000258 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000259
260 def test_ior(self):
261 self.s |= set(self.items2)
262 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000263 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000264
265 def test_intersection_update(self):
266 retval = self.s.intersection_update(self.items2)
267 self.assertEqual(retval, None)
268 for c in (self.items + self.items2):
269 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000270 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000271 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000272 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000273 self.assertRaises(TypeError, self.s.intersection_update, [[]])
274
275 def test_iand(self):
276 self.s &= set(self.items2)
277 for c in (self.items + self.items2):
278 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000279 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000280 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000281 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000282
283 def test_difference_update(self):
284 retval = self.s.difference_update(self.items2)
285 self.assertEqual(retval, None)
286 for c in (self.items + self.items2):
287 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000288 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000289 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000290 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000291 self.assertRaises(TypeError, self.s.difference_update, [[]])
292 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
293
294 def test_isub(self):
295 self.s -= set(self.items2)
296 for c in (self.items + self.items2):
297 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000298 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000299 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000300 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000301
302 def test_symmetric_difference_update(self):
303 retval = self.s.symmetric_difference_update(self.items2)
304 self.assertEqual(retval, None)
305 for c in (self.items + self.items2):
306 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000307 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000308 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000309 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000310 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
311
312 def test_ixor(self):
313 self.s ^= set(self.items2)
314 for c in (self.items + self.items2):
315 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000316 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000317 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000318 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000319
320 def test_inplace_on_self(self):
321 t = self.s.copy()
322 t |= t
323 self.assertEqual(t, self.s)
324 t &= t
325 self.assertEqual(t, self.s)
326 t -= t
327 self.assertEqual(t, WeakSet())
328 t = self.s.copy()
329 t ^= t
330 self.assertEqual(t, WeakSet())
331
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000332 def test_eq(self):
333 # issue 5964
334 self.assertTrue(self.s == self.s)
335 self.assertTrue(self.s == WeakSet(self.items))
336 self.assertFalse(self.s == set(self.items))
337 self.assertFalse(self.s == list(self.items))
338 self.assertFalse(self.s == tuple(self.items))
339 self.assertFalse(self.s == WeakSet([Foo]))
340 self.assertFalse(self.s == 1)
341
Benjamin Peterson67246122013-05-22 13:27:25 -0700342 def test_ne(self):
343 self.assertTrue(self.s != set(self.items))
344 s1 = WeakSet()
345 s2 = WeakSet()
346 self.assertFalse(s1 != s2)
347
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000348 def test_weak_destroy_while_iterating(self):
349 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
350 # Create new items to be sure no-one else holds a reference
351 items = [ustr(c) for c in ('a', 'b', 'c')]
352 s = WeakSet(items)
353 it = iter(s)
354 next(it) # Trigger internal iteration
355 # Destroy an item
356 del items[-1]
357 gc.collect() # just in case
358 # We have removed either the first consumed items, or another one
359 self.assertIn(len(list(it)), [len(items), len(items) - 1])
360 del it
361 # The removal has been committed
362 self.assertEqual(len(s), len(items))
363
364 def test_weak_destroy_and_mutate_while_iterating(self):
365 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
366 items = [ustr(c) for c in string.ascii_letters]
367 s = WeakSet(items)
368 @contextlib.contextmanager
369 def testcontext():
370 try:
371 it = iter(s)
Antoine Pitrou320b3912013-12-18 00:28:36 +0100372 # Start iterator
373 yielded = ustr(str(next(it)))
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000374 # Schedule an item for removal and recreate it
375 u = ustr(str(items.pop()))
Antoine Pitrou320b3912013-12-18 00:28:36 +0100376 if yielded == u:
377 # The iterator still has a reference to the removed item,
378 # advance it (issue #20006).
379 next(it)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000380 gc.collect() # just in case
381 yield u
382 finally:
383 it = None # should commit all removals
384
385 with testcontext() as u:
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000386 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000387 with testcontext() as u:
388 self.assertRaises(KeyError, s.remove, u)
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000389 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000390 with testcontext() as u:
391 s.add(u)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000392 self.assertIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000393 t = s.copy()
394 with testcontext() as u:
395 s.update(t)
396 self.assertEqual(len(s), len(t))
397 with testcontext() as u:
398 s.clear()
399 self.assertEqual(len(s), 0)
400
Antoine Pitroubbe2f602012-03-01 16:26:35 +0100401 def test_len_cycles(self):
402 N = 20
403 items = [RefCycle() for i in range(N)]
404 s = WeakSet(items)
405 del items
406 it = iter(s)
407 try:
408 next(it)
409 except StopIteration:
410 pass
411 gc.collect()
412 n1 = len(s)
413 del it
414 gc.collect()
415 n2 = len(s)
416 # one item may be kept alive inside the iterator
417 self.assertIn(n1, (0, 1))
418 self.assertEqual(n2, 0)
419
420 def test_len_race(self):
421 # Extended sanity checks for len() in the face of cyclic collection
422 self.addCleanup(gc.set_threshold, *gc.get_threshold())
423 for th in range(1, 100):
424 N = 20
425 gc.collect(0)
426 gc.set_threshold(th, th, th)
427 items = [RefCycle() for i in range(N)]
428 s = WeakSet(items)
429 del items
430 # All items will be collected at next garbage collection pass
431 it = iter(s)
432 try:
433 next(it)
434 except StopIteration:
435 pass
436 n1 = len(s)
437 del it
438 n2 = len(s)
439 self.assertGreaterEqual(n1, 0)
440 self.assertLessEqual(n1, N)
441 self.assertGreaterEqual(n2, 0)
442 self.assertLessEqual(n2, n1)
443
Georg Brandl9dba5d92008-05-18 16:27:29 +0000444
Georg Brandl9dba5d92008-05-18 16:27:29 +0000445if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500446 unittest.main()