blob: 4d3878f8a65babae833a64929701744488f6321e [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
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000343 def test_weak_destroy_while_iterating(self):
344 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
345 # Create new items to be sure no-one else holds a reference
346 items = [ustr(c) for c in ('a', 'b', 'c')]
347 s = WeakSet(items)
348 it = iter(s)
349 next(it) # Trigger internal iteration
350 # Destroy an item
351 del items[-1]
352 gc.collect() # just in case
353 # We have removed either the first consumed items, or another one
354 self.assertIn(len(list(it)), [len(items), len(items) - 1])
355 del it
356 # The removal has been committed
357 self.assertEqual(len(s), len(items))
358
359 def test_weak_destroy_and_mutate_while_iterating(self):
360 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
361 items = [ustr(c) for c in string.ascii_letters]
362 s = WeakSet(items)
363 @contextlib.contextmanager
364 def testcontext():
365 try:
366 it = iter(s)
367 next(it)
Antoine Pitrou95a97b72012-02-21 00:00:06 +0100368 del it
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000369 # Schedule an item for removal and recreate it
370 u = ustr(str(items.pop()))
371 gc.collect() # just in case
372 yield u
373 finally:
374 it = None # should commit all removals
375
376 with testcontext() as u:
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000377 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000378 with testcontext() as u:
379 self.assertRaises(KeyError, s.remove, u)
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000380 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000381 with testcontext() as u:
382 s.add(u)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000383 self.assertIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000384 t = s.copy()
385 with testcontext() as u:
386 s.update(t)
387 self.assertEqual(len(s), len(t))
388 with testcontext() as u:
389 s.clear()
390 self.assertEqual(len(s), 0)
391
Antoine Pitroubbe2f602012-03-01 16:26:35 +0100392 def test_len_cycles(self):
393 N = 20
394 items = [RefCycle() for i in range(N)]
395 s = WeakSet(items)
396 del items
397 it = iter(s)
398 try:
399 next(it)
400 except StopIteration:
401 pass
402 gc.collect()
403 n1 = len(s)
404 del it
405 gc.collect()
406 n2 = len(s)
407 # one item may be kept alive inside the iterator
408 self.assertIn(n1, (0, 1))
409 self.assertEqual(n2, 0)
410
411 def test_len_race(self):
412 # Extended sanity checks for len() in the face of cyclic collection
413 self.addCleanup(gc.set_threshold, *gc.get_threshold())
414 for th in range(1, 100):
415 N = 20
416 gc.collect(0)
417 gc.set_threshold(th, th, th)
418 items = [RefCycle() for i in range(N)]
419 s = WeakSet(items)
420 del items
421 # All items will be collected at next garbage collection pass
422 it = iter(s)
423 try:
424 next(it)
425 except StopIteration:
426 pass
427 n1 = len(s)
428 del it
429 n2 = len(s)
430 self.assertGreaterEqual(n1, 0)
431 self.assertLessEqual(n1, N)
432 self.assertGreaterEqual(n2, 0)
433 self.assertLessEqual(n2, n1)
434
Georg Brandl9dba5d92008-05-18 16:27:29 +0000435
436def test_main(verbose=None):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000437 support.run_unittest(TestWeakSet)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000438
439if __name__ == "__main__":
440 test_main(verbose=True)