blob: 3c71f62628c64c8f232b3a686e347fb3f58aefe2 [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)
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010074 del c
75 self.assertEqual(len(u), len(self.items) + len(self.items2))
76 self.items2.pop()
77 gc.collect()
78 self.assertEqual(len(u), len(self.items) + len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +000079
80 def test_or(self):
81 i = self.s.union(self.items2)
82 self.assertEqual(self.s | set(self.items2), i)
83 self.assertEqual(self.s | frozenset(self.items2), i)
84
85 def test_intersection(self):
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010086 s = WeakSet(self.letters)
87 i = s.intersection(self.items2)
Georg Brandl9dba5d92008-05-18 16:27:29 +000088 for c in self.letters:
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010089 self.assertEqual(c in i, c in self.items2 and c in self.letters)
90 self.assertEqual(s, WeakSet(self.letters))
Georg Brandl9dba5d92008-05-18 16:27:29 +000091 self.assertEqual(type(i), WeakSet)
92 for C in set, frozenset, dict.fromkeys, list, tuple:
93 x = WeakSet([])
Antoine Pitrou9c47ac02012-03-04 20:47:05 +010094 self.assertEqual(i.intersection(C(self.items)), x)
95 self.assertEqual(len(i), len(self.items2))
96 self.items2.pop()
97 gc.collect()
98 self.assertEqual(len(i), len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +000099
100 def test_isdisjoint(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000101 self.assertTrue(self.s.isdisjoint(WeakSet(self.items2)))
102 self.assertTrue(not self.s.isdisjoint(WeakSet(self.letters)))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000103
104 def test_and(self):
105 i = self.s.intersection(self.items2)
106 self.assertEqual(self.s & set(self.items2), i)
107 self.assertEqual(self.s & frozenset(self.items2), i)
108
109 def test_difference(self):
110 i = self.s.difference(self.items2)
111 for c in self.letters:
112 self.assertEqual(c in i, c in self.d and c not in self.items2)
113 self.assertEqual(self.s, WeakSet(self.items))
114 self.assertEqual(type(i), WeakSet)
115 self.assertRaises(TypeError, self.s.difference, [[]])
116
117 def test_sub(self):
118 i = self.s.difference(self.items2)
119 self.assertEqual(self.s - set(self.items2), i)
120 self.assertEqual(self.s - frozenset(self.items2), i)
121
122 def test_symmetric_difference(self):
123 i = self.s.symmetric_difference(self.items2)
124 for c in self.letters:
125 self.assertEqual(c in i, (c in self.d) ^ (c in self.items2))
126 self.assertEqual(self.s, WeakSet(self.items))
127 self.assertEqual(type(i), WeakSet)
128 self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
Antoine Pitrou9c47ac02012-03-04 20:47:05 +0100129 self.assertEqual(len(i), len(self.items) + len(self.items2))
130 self.items2.pop()
131 gc.collect()
132 self.assertEqual(len(i), len(self.items) + len(self.items2))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000133
134 def test_xor(self):
135 i = self.s.symmetric_difference(self.items2)
136 self.assertEqual(self.s ^ set(self.items2), i)
137 self.assertEqual(self.s ^ frozenset(self.items2), i)
138
139 def test_sub_and_super(self):
140 pl, ql, rl = map(lambda s: [ustr(c) for c in s], ['ab', 'abcde', 'def'])
141 p, q, r = map(WeakSet, (pl, ql, rl))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000142 self.assertTrue(p < q)
143 self.assertTrue(p <= q)
144 self.assertTrue(q <= q)
145 self.assertTrue(q > p)
146 self.assertTrue(q >= p)
147 self.assertFalse(q < r)
148 self.assertFalse(q <= r)
149 self.assertFalse(q > r)
150 self.assertFalse(q >= r)
151 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
156 def test_gc(self):
157 # Create a nest of cycles to exercise overall ref count check
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000158 s = WeakSet(Foo() for i in range(1000))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000159 for elem in s:
160 elem.cycle = s
161 elem.sub = elem
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000162 elem.set = WeakSet([elem])
Georg Brandl9dba5d92008-05-18 16:27:29 +0000163
164 def test_subclass_with_custom_hash(self):
165 # Bug #1257731
166 class H(WeakSet):
167 def __hash__(self):
168 return int(id(self) & 0x7fffffff)
169 s=H()
170 f=set()
171 f.add(s)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000172 self.assertIn(s, f)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000173 f.remove(s)
174 f.add(s)
175 f.discard(s)
176
177 def test_init(self):
178 s = WeakSet()
179 s.__init__(self.items)
180 self.assertEqual(s, self.s)
181 s.__init__(self.items2)
182 self.assertEqual(s, WeakSet(self.items2))
183 self.assertRaises(TypeError, s.__init__, s, 2);
184 self.assertRaises(TypeError, s.__init__, 1);
185
186 def test_constructor_identity(self):
187 s = WeakSet(self.items)
188 t = WeakSet(s)
189 self.assertNotEqual(id(s), id(t))
190
Georg Brandl9dba5d92008-05-18 16:27:29 +0000191 def test_hash(self):
192 self.assertRaises(TypeError, hash, self.s)
193
194 def test_clear(self):
195 self.s.clear()
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000196 self.assertEqual(self.s, WeakSet([]))
Georg Brandl9dba5d92008-05-18 16:27:29 +0000197 self.assertEqual(len(self.s), 0)
198
199 def test_copy(self):
200 dup = self.s.copy()
201 self.assertEqual(self.s, dup)
202 self.assertNotEqual(id(self.s), id(dup))
203
204 def test_add(self):
205 x = ustr('Q')
206 self.s.add(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000207 self.assertIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000208 dup = self.s.copy()
209 self.s.add(x)
210 self.assertEqual(self.s, dup)
211 self.assertRaises(TypeError, self.s.add, [])
212 self.fs.add(Foo())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000213 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000214 self.fs.add(self.obj)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000215 self.assertTrue(len(self.fs) == 1)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000216
217 def test_remove(self):
218 x = ustr('a')
219 self.s.remove(x)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000220 self.assertNotIn(x, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000221 self.assertRaises(KeyError, self.s.remove, x)
222 self.assertRaises(TypeError, self.s.remove, [])
223
224 def test_discard(self):
225 a, q = ustr('a'), ustr('Q')
226 self.s.discard(a)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000227 self.assertNotIn(a, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000228 self.s.discard(q)
229 self.assertRaises(TypeError, self.s.discard, [])
230
231 def test_pop(self):
232 for i in range(len(self.s)):
233 elem = self.s.pop()
Benjamin Peterson577473f2010-01-19 00:09:57 +0000234 self.assertNotIn(elem, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000235 self.assertRaises(KeyError, self.s.pop)
236
237 def test_update(self):
238 retval = self.s.update(self.items2)
239 self.assertEqual(retval, None)
240 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000241 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000242 self.assertRaises(TypeError, self.s.update, [[]])
243
244 def test_update_set(self):
245 self.s.update(set(self.items2))
246 for c in (self.items + self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000247 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000248
249 def test_ior(self):
250 self.s |= set(self.items2)
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
254 def test_intersection_update(self):
255 retval = self.s.intersection_update(self.items2)
256 self.assertEqual(retval, None)
257 for c in (self.items + self.items2):
258 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000259 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000260 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000261 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000262 self.assertRaises(TypeError, self.s.intersection_update, [[]])
263
264 def test_iand(self):
265 self.s &= set(self.items2)
266 for c in (self.items + self.items2):
267 if c in self.items2 and c in self.items:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000268 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000269 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000270 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000271
272 def test_difference_update(self):
273 retval = self.s.difference_update(self.items2)
274 self.assertEqual(retval, None)
275 for c in (self.items + self.items2):
276 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000277 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000278 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000279 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000280 self.assertRaises(TypeError, self.s.difference_update, [[]])
281 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
282
283 def test_isub(self):
284 self.s -= set(self.items2)
285 for c in (self.items + self.items2):
286 if c in self.items and c not in self.items2:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000287 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000288 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000289 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000290
291 def test_symmetric_difference_update(self):
292 retval = self.s.symmetric_difference_update(self.items2)
293 self.assertEqual(retval, None)
294 for c in (self.items + self.items2):
295 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000296 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000297 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000298 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000299 self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]])
300
301 def test_ixor(self):
302 self.s ^= set(self.items2)
303 for c in (self.items + self.items2):
304 if (c in self.items) ^ (c in self.items2):
Benjamin Peterson577473f2010-01-19 00:09:57 +0000305 self.assertIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000306 else:
Benjamin Peterson577473f2010-01-19 00:09:57 +0000307 self.assertNotIn(c, self.s)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000308
309 def test_inplace_on_self(self):
310 t = self.s.copy()
311 t |= t
312 self.assertEqual(t, self.s)
313 t &= t
314 self.assertEqual(t, self.s)
315 t -= t
316 self.assertEqual(t, WeakSet())
317 t = self.s.copy()
318 t ^= t
319 self.assertEqual(t, WeakSet())
320
Robert Schuppenies4ad1d6f2009-05-17 17:32:20 +0000321 def test_eq(self):
322 # issue 5964
323 self.assertTrue(self.s == self.s)
324 self.assertTrue(self.s == WeakSet(self.items))
325 self.assertFalse(self.s == set(self.items))
326 self.assertFalse(self.s == list(self.items))
327 self.assertFalse(self.s == tuple(self.items))
328 self.assertFalse(self.s == WeakSet([Foo]))
329 self.assertFalse(self.s == 1)
330
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000331 def test_weak_destroy_while_iterating(self):
332 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
333 # Create new items to be sure no-one else holds a reference
334 items = [ustr(c) for c in ('a', 'b', 'c')]
335 s = WeakSet(items)
336 it = iter(s)
337 next(it) # Trigger internal iteration
338 # Destroy an item
339 del items[-1]
340 gc.collect() # just in case
341 # We have removed either the first consumed items, or another one
342 self.assertIn(len(list(it)), [len(items), len(items) - 1])
343 del it
344 # The removal has been committed
345 self.assertEqual(len(s), len(items))
346
347 def test_weak_destroy_and_mutate_while_iterating(self):
348 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
349 items = [ustr(c) for c in string.ascii_letters]
350 s = WeakSet(items)
351 @contextlib.contextmanager
352 def testcontext():
353 try:
354 it = iter(s)
355 next(it)
Antoine Pitrou95a97b72012-02-21 00:00:06 +0100356 del it
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000357 # Schedule an item for removal and recreate it
358 u = ustr(str(items.pop()))
359 gc.collect() # just in case
360 yield u
361 finally:
362 it = None # should commit all removals
363
364 with testcontext() as u:
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000365 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000366 with testcontext() as u:
367 self.assertRaises(KeyError, s.remove, u)
Ezio Melottib58e0bd2010-01-23 15:40:09 +0000368 self.assertNotIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000369 with testcontext() as u:
370 s.add(u)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000371 self.assertIn(u, s)
Antoine Pitrouc1baa602010-01-08 17:54:23 +0000372 t = s.copy()
373 with testcontext() as u:
374 s.update(t)
375 self.assertEqual(len(s), len(t))
376 with testcontext() as u:
377 s.clear()
378 self.assertEqual(len(s), 0)
379
Antoine Pitroubbe2f602012-03-01 16:26:35 +0100380 def test_len_cycles(self):
381 N = 20
382 items = [RefCycle() for i in range(N)]
383 s = WeakSet(items)
384 del items
385 it = iter(s)
386 try:
387 next(it)
388 except StopIteration:
389 pass
390 gc.collect()
391 n1 = len(s)
392 del it
393 gc.collect()
394 n2 = len(s)
395 # one item may be kept alive inside the iterator
396 self.assertIn(n1, (0, 1))
397 self.assertEqual(n2, 0)
398
399 def test_len_race(self):
400 # Extended sanity checks for len() in the face of cyclic collection
401 self.addCleanup(gc.set_threshold, *gc.get_threshold())
402 for th in range(1, 100):
403 N = 20
404 gc.collect(0)
405 gc.set_threshold(th, th, th)
406 items = [RefCycle() for i in range(N)]
407 s = WeakSet(items)
408 del items
409 # All items will be collected at next garbage collection pass
410 it = iter(s)
411 try:
412 next(it)
413 except StopIteration:
414 pass
415 n1 = len(s)
416 del it
417 n2 = len(s)
418 self.assertGreaterEqual(n1, 0)
419 self.assertLessEqual(n1, N)
420 self.assertGreaterEqual(n2, 0)
421 self.assertLessEqual(n2, n1)
422
Georg Brandl9dba5d92008-05-18 16:27:29 +0000423
424def test_main(verbose=None):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000425 support.run_unittest(TestWeakSet)
Georg Brandl9dba5d92008-05-18 16:27:29 +0000426
427if __name__ == "__main__":
428 test_main(verbose=True)