Issue #3680: Reference cycles created through a dict, set or deque iterator did not...
[python.git] / Lib / test / test_dict.py
blob14d62f5024b7c209fe5eb1b9128c75ba4fcd04f7
1 import unittest
2 from test import test_support
4 import UserDict, random, string
5 import gc, weakref
8 class DictTest(unittest.TestCase):
9 def test_constructor(self):
10 # calling built-in types without argument must return empty
11 self.assertEqual(dict(), {})
12 self.assert_(dict() is not {})
14 def test_literal_constructor(self):
15 # check literal constructor for different sized dicts (to exercise the BUILD_MAP oparg
16 for n in (0, 1, 6, 256, 400):
17 items = [(''.join([random.choice(string.letters)
18 for j in range(8)]),
20 for i in range(n)]
21 random.shuffle(items)
22 dictliteral = '{' + ', '.join('%r: %d' % item for item in items) + '}'
23 self.assertEqual(eval(dictliteral), dict(items))
25 def test_bool(self):
26 self.assert_(not {})
27 self.assert_({1: 2})
28 self.assert_(bool({}) is False)
29 self.assert_(bool({1: 2}) is True)
31 def test_keys(self):
32 d = {}
33 self.assertEqual(d.keys(), [])
34 d = {'a': 1, 'b': 2}
35 k = d.keys()
36 self.assert_(d.has_key('a'))
37 self.assert_(d.has_key('b'))
39 self.assertRaises(TypeError, d.keys, None)
41 def test_values(self):
42 d = {}
43 self.assertEqual(d.values(), [])
44 d = {1:2}
45 self.assertEqual(d.values(), [2])
47 self.assertRaises(TypeError, d.values, None)
49 def test_items(self):
50 d = {}
51 self.assertEqual(d.items(), [])
53 d = {1:2}
54 self.assertEqual(d.items(), [(1, 2)])
56 self.assertRaises(TypeError, d.items, None)
58 def test_has_key(self):
59 d = {}
60 self.assert_(not d.has_key('a'))
61 d = {'a': 1, 'b': 2}
62 k = d.keys()
63 k.sort()
64 self.assertEqual(k, ['a', 'b'])
66 self.assertRaises(TypeError, d.has_key)
68 def test_contains(self):
69 d = {}
70 self.assert_(not ('a' in d))
71 self.assert_('a' not in d)
72 d = {'a': 1, 'b': 2}
73 self.assert_('a' in d)
74 self.assert_('b' in d)
75 self.assert_('c' not in d)
77 self.assertRaises(TypeError, d.__contains__)
79 def test_len(self):
80 d = {}
81 self.assertEqual(len(d), 0)
82 d = {'a': 1, 'b': 2}
83 self.assertEqual(len(d), 2)
85 def test_getitem(self):
86 d = {'a': 1, 'b': 2}
87 self.assertEqual(d['a'], 1)
88 self.assertEqual(d['b'], 2)
89 d['c'] = 3
90 d['a'] = 4
91 self.assertEqual(d['c'], 3)
92 self.assertEqual(d['a'], 4)
93 del d['b']
94 self.assertEqual(d, {'a': 4, 'c': 3})
96 self.assertRaises(TypeError, d.__getitem__)
98 class BadEq(object):
99 def __eq__(self, other):
100 raise Exc()
101 def __hash__(self):
102 return 24
104 d = {}
105 d[BadEq()] = 42
106 self.assertRaises(KeyError, d.__getitem__, 23)
108 class Exc(Exception): pass
110 class BadHash(object):
111 fail = False
112 def __hash__(self):
113 if self.fail:
114 raise Exc()
115 else:
116 return 42
118 x = BadHash()
119 d[x] = 42
120 x.fail = True
121 self.assertRaises(Exc, d.__getitem__, x)
123 def test_clear(self):
124 d = {1:1, 2:2, 3:3}
125 d.clear()
126 self.assertEqual(d, {})
128 self.assertRaises(TypeError, d.clear, None)
130 def test_update(self):
131 d = {}
132 d.update({1:100})
133 d.update({2:20})
134 d.update({1:1, 2:2, 3:3})
135 self.assertEqual(d, {1:1, 2:2, 3:3})
137 d.update()
138 self.assertEqual(d, {1:1, 2:2, 3:3})
140 self.assertRaises((TypeError, AttributeError), d.update, None)
142 class SimpleUserDict:
143 def __init__(self):
144 self.d = {1:1, 2:2, 3:3}
145 def keys(self):
146 return self.d.keys()
147 def __getitem__(self, i):
148 return self.d[i]
149 d.clear()
150 d.update(SimpleUserDict())
151 self.assertEqual(d, {1:1, 2:2, 3:3})
153 class Exc(Exception): pass
155 d.clear()
156 class FailingUserDict:
157 def keys(self):
158 raise Exc
159 self.assertRaises(Exc, d.update, FailingUserDict())
161 class FailingUserDict:
162 def keys(self):
163 class BogonIter:
164 def __init__(self):
165 self.i = 1
166 def __iter__(self):
167 return self
168 def next(self):
169 if self.i:
170 self.i = 0
171 return 'a'
172 raise Exc
173 return BogonIter()
174 def __getitem__(self, key):
175 return key
176 self.assertRaises(Exc, d.update, FailingUserDict())
178 class FailingUserDict:
179 def keys(self):
180 class BogonIter:
181 def __init__(self):
182 self.i = ord('a')
183 def __iter__(self):
184 return self
185 def next(self):
186 if self.i <= ord('z'):
187 rtn = chr(self.i)
188 self.i += 1
189 return rtn
190 raise StopIteration
191 return BogonIter()
192 def __getitem__(self, key):
193 raise Exc
194 self.assertRaises(Exc, d.update, FailingUserDict())
196 class badseq(object):
197 def __iter__(self):
198 return self
199 def next(self):
200 raise Exc()
202 self.assertRaises(Exc, {}.update, badseq())
204 self.assertRaises(ValueError, {}.update, [(1, 2, 3)])
206 def test_fromkeys(self):
207 self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
208 d = {}
209 self.assert_(not(d.fromkeys('abc') is d))
210 self.assertEqual(d.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
211 self.assertEqual(d.fromkeys((4,5),0), {4:0, 5:0})
212 self.assertEqual(d.fromkeys([]), {})
213 def g():
214 yield 1
215 self.assertEqual(d.fromkeys(g()), {1:None})
216 self.assertRaises(TypeError, {}.fromkeys, 3)
217 class dictlike(dict): pass
218 self.assertEqual(dictlike.fromkeys('a'), {'a':None})
219 self.assertEqual(dictlike().fromkeys('a'), {'a':None})
220 self.assert_(type(dictlike.fromkeys('a')) is dictlike)
221 self.assert_(type(dictlike().fromkeys('a')) is dictlike)
222 class mydict(dict):
223 def __new__(cls):
224 return UserDict.UserDict()
225 ud = mydict.fromkeys('ab')
226 self.assertEqual(ud, {'a':None, 'b':None})
227 self.assert_(isinstance(ud, UserDict.UserDict))
228 self.assertRaises(TypeError, dict.fromkeys)
230 class Exc(Exception): pass
232 class baddict1(dict):
233 def __init__(self):
234 raise Exc()
236 self.assertRaises(Exc, baddict1.fromkeys, [1])
238 class BadSeq(object):
239 def __iter__(self):
240 return self
241 def next(self):
242 raise Exc()
244 self.assertRaises(Exc, dict.fromkeys, BadSeq())
246 class baddict2(dict):
247 def __setitem__(self, key, value):
248 raise Exc()
250 self.assertRaises(Exc, baddict2.fromkeys, [1])
252 # test fast path for dictionary inputs
253 d = dict(zip(range(6), range(6)))
254 self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6)))
256 def test_copy(self):
257 d = {1:1, 2:2, 3:3}
258 self.assertEqual(d.copy(), {1:1, 2:2, 3:3})
259 self.assertEqual({}.copy(), {})
260 self.assertRaises(TypeError, d.copy, None)
262 def test_get(self):
263 d = {}
264 self.assert_(d.get('c') is None)
265 self.assertEqual(d.get('c', 3), 3)
266 d = {'a' : 1, 'b' : 2}
267 self.assert_(d.get('c') is None)
268 self.assertEqual(d.get('c', 3), 3)
269 self.assertEqual(d.get('a'), 1)
270 self.assertEqual(d.get('a', 3), 1)
271 self.assertRaises(TypeError, d.get)
272 self.assertRaises(TypeError, d.get, None, None, None)
274 def test_setdefault(self):
275 # dict.setdefault()
276 d = {}
277 self.assert_(d.setdefault('key0') is None)
278 d.setdefault('key0', [])
279 self.assert_(d.setdefault('key0') is None)
280 d.setdefault('key', []).append(3)
281 self.assertEqual(d['key'][0], 3)
282 d.setdefault('key', []).append(4)
283 self.assertEqual(len(d['key']), 2)
284 self.assertRaises(TypeError, d.setdefault)
286 class Exc(Exception): pass
288 class BadHash(object):
289 fail = False
290 def __hash__(self):
291 if self.fail:
292 raise Exc()
293 else:
294 return 42
296 x = BadHash()
297 d[x] = 42
298 x.fail = True
299 self.assertRaises(Exc, d.setdefault, x, [])
301 def test_popitem(self):
302 # dict.popitem()
303 for copymode in -1, +1:
304 # -1: b has same structure as a
305 # +1: b is a.copy()
306 for log2size in range(12):
307 size = 2**log2size
308 a = {}
309 b = {}
310 for i in range(size):
311 a[repr(i)] = i
312 if copymode < 0:
313 b[repr(i)] = i
314 if copymode > 0:
315 b = a.copy()
316 for i in range(size):
317 ka, va = ta = a.popitem()
318 self.assertEqual(va, int(ka))
319 kb, vb = tb = b.popitem()
320 self.assertEqual(vb, int(kb))
321 self.assert_(not(copymode < 0 and ta != tb))
322 self.assert_(not a)
323 self.assert_(not b)
325 d = {}
326 self.assertRaises(KeyError, d.popitem)
328 def test_pop(self):
329 # Tests for pop with specified key
330 d = {}
331 k, v = 'abc', 'def'
332 d[k] = v
333 self.assertRaises(KeyError, d.pop, 'ghi')
335 self.assertEqual(d.pop(k), v)
336 self.assertEqual(len(d), 0)
338 self.assertRaises(KeyError, d.pop, k)
340 # verify longs/ints get same value when key > 32 bits (for 64-bit archs)
341 # see SF bug #689659
342 x = 4503599627370496L
343 y = 4503599627370496
344 h = {x: 'anything', y: 'something else'}
345 self.assertEqual(h[x], h[y])
347 self.assertEqual(d.pop(k, v), v)
348 d[k] = v
349 self.assertEqual(d.pop(k, 1), v)
351 self.assertRaises(TypeError, d.pop)
353 class Exc(Exception): pass
355 class BadHash(object):
356 fail = False
357 def __hash__(self):
358 if self.fail:
359 raise Exc()
360 else:
361 return 42
363 x = BadHash()
364 d[x] = 42
365 x.fail = True
366 self.assertRaises(Exc, d.pop, x)
368 def test_mutatingiteration(self):
369 d = {}
370 d[1] = 1
371 try:
372 for i in d:
373 d[i+1] = 1
374 except RuntimeError:
375 pass
376 else:
377 self.fail("changing dict size during iteration doesn't raise Error")
379 def test_repr(self):
380 d = {}
381 self.assertEqual(repr(d), '{}')
382 d[1] = 2
383 self.assertEqual(repr(d), '{1: 2}')
384 d = {}
385 d[1] = d
386 self.assertEqual(repr(d), '{1: {...}}')
388 class Exc(Exception): pass
390 class BadRepr(object):
391 def __repr__(self):
392 raise Exc()
394 d = {1: BadRepr()}
395 self.assertRaises(Exc, repr, d)
397 def test_le(self):
398 self.assert_(not ({} < {}))
399 self.assert_(not ({1: 2} < {1L: 2L}))
401 class Exc(Exception): pass
403 class BadCmp(object):
404 def __eq__(self, other):
405 raise Exc()
406 def __hash__(self):
407 return 42
409 d1 = {BadCmp(): 1}
410 d2 = {1: 1}
411 try:
412 d1 < d2
413 except Exc:
414 pass
415 else:
416 self.fail("< didn't raise Exc")
418 def test_missing(self):
419 # Make sure dict doesn't have a __missing__ method
420 self.assertEqual(hasattr(dict, "__missing__"), False)
421 self.assertEqual(hasattr({}, "__missing__"), False)
422 # Test several cases:
423 # (D) subclass defines __missing__ method returning a value
424 # (E) subclass defines __missing__ method raising RuntimeError
425 # (F) subclass sets __missing__ instance variable (no effect)
426 # (G) subclass doesn't define __missing__ at a all
427 class D(dict):
428 def __missing__(self, key):
429 return 42
430 d = D({1: 2, 3: 4})
431 self.assertEqual(d[1], 2)
432 self.assertEqual(d[3], 4)
433 self.assert_(2 not in d)
434 self.assert_(2 not in d.keys())
435 self.assertEqual(d[2], 42)
436 class E(dict):
437 def __missing__(self, key):
438 raise RuntimeError(key)
439 e = E()
440 try:
441 e[42]
442 except RuntimeError, err:
443 self.assertEqual(err.args, (42,))
444 else:
445 self.fail("e[42] didn't raise RuntimeError")
446 class F(dict):
447 def __init__(self):
448 # An instance variable __missing__ should have no effect
449 self.__missing__ = lambda key: None
450 f = F()
451 try:
452 f[42]
453 except KeyError, err:
454 self.assertEqual(err.args, (42,))
455 else:
456 self.fail("f[42] didn't raise KeyError")
457 class G(dict):
458 pass
459 g = G()
460 try:
461 g[42]
462 except KeyError, err:
463 self.assertEqual(err.args, (42,))
464 else:
465 self.fail("g[42] didn't raise KeyError")
467 def test_tuple_keyerror(self):
468 # SF #1576657
469 d = {}
470 try:
471 d[(1,)]
472 except KeyError, e:
473 self.assertEqual(e.args, ((1,),))
474 else:
475 self.fail("missing KeyError")
477 def test_bad_key(self):
478 # Dictionary lookups should fail if __cmp__() raises an exception.
479 class CustomException(Exception):
480 pass
482 class BadDictKey:
483 def __hash__(self):
484 return hash(self.__class__)
486 def __cmp__(self, other):
487 if isinstance(other, self.__class__):
488 raise CustomException
489 return other
491 d = {}
492 x1 = BadDictKey()
493 x2 = BadDictKey()
494 d[x1] = 1
495 for stmt in ['d[x2] = 2',
496 'z = d[x2]',
497 'x2 in d',
498 'd.has_key(x2)',
499 'd.get(x2)',
500 'd.setdefault(x2, 42)',
501 'd.pop(x2)',
502 'd.update({x2: 2})']:
503 try:
504 exec stmt in locals()
505 except CustomException:
506 pass
507 else:
508 self.fail("Statement didn't raise exception")
510 def test_resize1(self):
511 # Dict resizing bug, found by Jack Jansen in 2.2 CVS development.
512 # This version got an assert failure in debug build, infinite loop in
513 # release build. Unfortunately, provoking this kind of stuff requires
514 # a mix of inserts and deletes hitting exactly the right hash codes in
515 # exactly the right order, and I can't think of a randomized approach
516 # that would be *likely* to hit a failing case in reasonable time.
518 d = {}
519 for i in range(5):
520 d[i] = i
521 for i in range(5):
522 del d[i]
523 for i in range(5, 9): # i==8 was the problem
524 d[i] = i
526 def test_resize2(self):
527 # Another dict resizing bug (SF bug #1456209).
528 # This caused Segmentation faults or Illegal instructions.
530 class X(object):
531 def __hash__(self):
532 return 5
533 def __eq__(self, other):
534 if resizing:
535 d.clear()
536 return False
537 d = {}
538 resizing = False
539 d[X()] = 1
540 d[X()] = 2
541 d[X()] = 3
542 d[X()] = 4
543 d[X()] = 5
544 # now trigger a resize
545 resizing = True
546 d[9] = 6
548 def test_empty_presized_dict_in_freelist(self):
549 # Bug #3537: if an empty but presized dict with a size larger
550 # than 7 was in the freelist, it triggered an assertion failure
551 try:
552 d = {'a': 1/0, 'b': None, 'c': None, 'd': None, 'e': None,
553 'f': None, 'g': None, 'h': None}
554 except ZeroDivisionError:
555 pass
556 d = {}
558 def test_container_iterator(self):
559 # Bug # XXX: tp_traverse was not implemented for dictiter objects
560 class C(object):
561 pass
562 iterators = (dict.iteritems, dict.itervalues, dict.iterkeys)
563 for i in iterators:
564 obj = C()
565 ref = weakref.ref(obj)
566 container = {obj: 1}
567 obj.x = i(container)
568 del obj, container
569 gc.collect()
570 self.assert_(ref() is None, "Cycle was not collected")
573 from test import mapping_tests
575 class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
576 type2test = dict
578 class Dict(dict):
579 pass
581 class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
582 type2test = Dict
584 def test_main():
585 test_support.run_unittest(
586 DictTest,
587 GeneralMappingTests,
588 SubclassMappingTests,
591 if __name__ == "__main__":
592 test_main()