python (3.12.0)
1 import collections
2 import collections.abc
3 import gc
4 import pickle
5 import random
6 import string
7 import sys
8 import unittest
9 import weakref
10 from test import support
11 from test.support import import_helper, C_RECURSION_LIMIT
12
13
14 class ESC[4;38;5;81mDictTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
15
16 def test_invalid_keyword_arguments(self):
17 class ESC[4;38;5;81mCustom(ESC[4;38;5;149mdict):
18 pass
19 for invalid in {1 : 2}, Custom({1 : 2}):
20 with self.assertRaises(TypeError):
21 dict(**invalid)
22 with self.assertRaises(TypeError):
23 {}.update(**invalid)
24
25 def test_constructor(self):
26 # calling built-in types without argument must return empty
27 self.assertEqual(dict(), {})
28 self.assertIsNot(dict(), {})
29
30 def test_literal_constructor(self):
31 # check literal constructor for different sized dicts
32 # (to exercise the BUILD_MAP oparg).
33 for n in (0, 1, 6, 256, 400):
34 items = [(''.join(random.sample(string.ascii_letters, 8)), i)
35 for i in range(n)]
36 random.shuffle(items)
37 formatted_items = ('{!r}: {:d}'.format(k, v) for k, v in items)
38 dictliteral = '{' + ', '.join(formatted_items) + '}'
39 self.assertEqual(eval(dictliteral), dict(items))
40
41 def test_merge_operator(self):
42
43 a = {0: 0, 1: 1, 2: 1}
44 b = {1: 1, 2: 2, 3: 3}
45
46 c = a.copy()
47 c |= b
48
49 self.assertEqual(a | b, {0: 0, 1: 1, 2: 2, 3: 3})
50 self.assertEqual(c, {0: 0, 1: 1, 2: 2, 3: 3})
51
52 c = b.copy()
53 c |= a
54
55 self.assertEqual(b | a, {1: 1, 2: 1, 3: 3, 0: 0})
56 self.assertEqual(c, {1: 1, 2: 1, 3: 3, 0: 0})
57
58 c = a.copy()
59 c |= [(1, 1), (2, 2), (3, 3)]
60
61 self.assertEqual(c, {0: 0, 1: 1, 2: 2, 3: 3})
62
63 self.assertIs(a.__or__(None), NotImplemented)
64 self.assertIs(a.__or__(()), NotImplemented)
65 self.assertIs(a.__or__("BAD"), NotImplemented)
66 self.assertIs(a.__or__(""), NotImplemented)
67
68 self.assertRaises(TypeError, a.__ior__, None)
69 self.assertEqual(a.__ior__(()), {0: 0, 1: 1, 2: 1})
70 self.assertRaises(ValueError, a.__ior__, "BAD")
71 self.assertEqual(a.__ior__(""), {0: 0, 1: 1, 2: 1})
72
73 def test_bool(self):
74 self.assertIs(not {}, True)
75 self.assertTrue({1: 2})
76 self.assertIs(bool({}), False)
77 self.assertIs(bool({1: 2}), True)
78
79 def test_keys(self):
80 d = {}
81 self.assertEqual(set(d.keys()), set())
82 d = {'a': 1, 'b': 2}
83 k = d.keys()
84 self.assertEqual(set(k), {'a', 'b'})
85 self.assertIn('a', k)
86 self.assertIn('b', k)
87 self.assertIn('a', d)
88 self.assertIn('b', d)
89 self.assertRaises(TypeError, d.keys, None)
90 self.assertEqual(repr(dict(a=1).keys()), "dict_keys(['a'])")
91
92 def test_values(self):
93 d = {}
94 self.assertEqual(set(d.values()), set())
95 d = {1:2}
96 self.assertEqual(set(d.values()), {2})
97 self.assertRaises(TypeError, d.values, None)
98 self.assertEqual(repr(dict(a=1).values()), "dict_values([1])")
99
100 def test_items(self):
101 d = {}
102 self.assertEqual(set(d.items()), set())
103
104 d = {1:2}
105 self.assertEqual(set(d.items()), {(1, 2)})
106 self.assertRaises(TypeError, d.items, None)
107 self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
108
109 def test_views_mapping(self):
110 mappingproxy = type(type.__dict__)
111 class ESC[4;38;5;81mDict(ESC[4;38;5;149mdict):
112 pass
113 for cls in [dict, Dict]:
114 d = cls()
115 m1 = d.keys().mapping
116 m2 = d.values().mapping
117 m3 = d.items().mapping
118
119 for m in [m1, m2, m3]:
120 self.assertIsInstance(m, mappingproxy)
121 self.assertEqual(m, d)
122
123 d["foo"] = "bar"
124
125 for m in [m1, m2, m3]:
126 self.assertIsInstance(m, mappingproxy)
127 self.assertEqual(m, d)
128
129 def test_contains(self):
130 d = {}
131 self.assertNotIn('a', d)
132 self.assertFalse('a' in d)
133 self.assertTrue('a' not in d)
134 d = {'a': 1, 'b': 2}
135 self.assertIn('a', d)
136 self.assertIn('b', d)
137 self.assertNotIn('c', d)
138
139 self.assertRaises(TypeError, d.__contains__)
140
141 def test_len(self):
142 d = {}
143 self.assertEqual(len(d), 0)
144 d = {'a': 1, 'b': 2}
145 self.assertEqual(len(d), 2)
146
147 def test_getitem(self):
148 d = {'a': 1, 'b': 2}
149 self.assertEqual(d['a'], 1)
150 self.assertEqual(d['b'], 2)
151 d['c'] = 3
152 d['a'] = 4
153 self.assertEqual(d['c'], 3)
154 self.assertEqual(d['a'], 4)
155 del d['b']
156 self.assertEqual(d, {'a': 4, 'c': 3})
157
158 self.assertRaises(TypeError, d.__getitem__)
159
160 class ESC[4;38;5;81mBadEq(ESC[4;38;5;149mobject):
161 def __eq__(self, other):
162 raise Exc()
163 def __hash__(self):
164 return 24
165
166 d = {}
167 d[BadEq()] = 42
168 self.assertRaises(KeyError, d.__getitem__, 23)
169
170 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
171
172 class ESC[4;38;5;81mBadHash(ESC[4;38;5;149mobject):
173 fail = False
174 def __hash__(self):
175 if self.fail:
176 raise Exc()
177 else:
178 return 42
179
180 x = BadHash()
181 d[x] = 42
182 x.fail = True
183 self.assertRaises(Exc, d.__getitem__, x)
184
185 def test_clear(self):
186 d = {1:1, 2:2, 3:3}
187 d.clear()
188 self.assertEqual(d, {})
189
190 self.assertRaises(TypeError, d.clear, None)
191
192 def test_update(self):
193 d = {}
194 d.update({1:100})
195 d.update({2:20})
196 d.update({1:1, 2:2, 3:3})
197 self.assertEqual(d, {1:1, 2:2, 3:3})
198
199 d.update()
200 self.assertEqual(d, {1:1, 2:2, 3:3})
201
202 self.assertRaises((TypeError, AttributeError), d.update, None)
203
204 class ESC[4;38;5;81mSimpleUserDict:
205 def __init__(self):
206 self.d = {1:1, 2:2, 3:3}
207 def keys(self):
208 return self.d.keys()
209 def __getitem__(self, i):
210 return self.d[i]
211 d.clear()
212 d.update(SimpleUserDict())
213 self.assertEqual(d, {1:1, 2:2, 3:3})
214
215 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
216
217 d.clear()
218 class ESC[4;38;5;81mFailingUserDict:
219 def keys(self):
220 raise Exc
221 self.assertRaises(Exc, d.update, FailingUserDict())
222
223 class ESC[4;38;5;81mFailingUserDict:
224 def keys(self):
225 class ESC[4;38;5;81mBogonIter:
226 def __init__(self):
227 self.i = 1
228 def __iter__(self):
229 return self
230 def __next__(self):
231 if self.i:
232 self.i = 0
233 return 'a'
234 raise Exc
235 return BogonIter()
236 def __getitem__(self, key):
237 return key
238 self.assertRaises(Exc, d.update, FailingUserDict())
239
240 class ESC[4;38;5;81mFailingUserDict:
241 def keys(self):
242 class ESC[4;38;5;81mBogonIter:
243 def __init__(self):
244 self.i = ord('a')
245 def __iter__(self):
246 return self
247 def __next__(self):
248 if self.i <= ord('z'):
249 rtn = chr(self.i)
250 self.i += 1
251 return rtn
252 raise StopIteration
253 return BogonIter()
254 def __getitem__(self, key):
255 raise Exc
256 self.assertRaises(Exc, d.update, FailingUserDict())
257
258 class ESC[4;38;5;81mbadseq(ESC[4;38;5;149mobject):
259 def __iter__(self):
260 return self
261 def __next__(self):
262 raise Exc()
263
264 self.assertRaises(Exc, {}.update, badseq())
265
266 self.assertRaises(ValueError, {}.update, [(1, 2, 3)])
267
268 def test_fromkeys(self):
269 self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
270 d = {}
271 self.assertIsNot(d.fromkeys('abc'), d)
272 self.assertEqual(d.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
273 self.assertEqual(d.fromkeys((4,5),0), {4:0, 5:0})
274 self.assertEqual(d.fromkeys([]), {})
275 def g():
276 yield 1
277 self.assertEqual(d.fromkeys(g()), {1:None})
278 self.assertRaises(TypeError, {}.fromkeys, 3)
279 class ESC[4;38;5;81mdictlike(ESC[4;38;5;149mdict): pass
280 self.assertEqual(dictlike.fromkeys('a'), {'a':None})
281 self.assertEqual(dictlike().fromkeys('a'), {'a':None})
282 self.assertIsInstance(dictlike.fromkeys('a'), dictlike)
283 self.assertIsInstance(dictlike().fromkeys('a'), dictlike)
284 class ESC[4;38;5;81mmydict(ESC[4;38;5;149mdict):
285 def __new__(cls):
286 return collections.UserDict()
287 ud = mydict.fromkeys('ab')
288 self.assertEqual(ud, {'a':None, 'b':None})
289 self.assertIsInstance(ud, collections.UserDict)
290 self.assertRaises(TypeError, dict.fromkeys)
291
292 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
293
294 class ESC[4;38;5;81mbaddict1(ESC[4;38;5;149mdict):
295 def __init__(self):
296 raise Exc()
297
298 self.assertRaises(Exc, baddict1.fromkeys, [1])
299
300 class ESC[4;38;5;81mBadSeq(ESC[4;38;5;149mobject):
301 def __iter__(self):
302 return self
303 def __next__(self):
304 raise Exc()
305
306 self.assertRaises(Exc, dict.fromkeys, BadSeq())
307
308 class ESC[4;38;5;81mbaddict2(ESC[4;38;5;149mdict):
309 def __setitem__(self, key, value):
310 raise Exc()
311
312 self.assertRaises(Exc, baddict2.fromkeys, [1])
313
314 # test fast path for dictionary inputs
315 d = dict(zip(range(6), range(6)))
316 self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6)))
317
318 class ESC[4;38;5;81mbaddict3(ESC[4;38;5;149mdict):
319 def __new__(cls):
320 return d
321 d = {i : i for i in range(10)}
322 res = d.copy()
323 res.update(a=None, b=None, c=None)
324 self.assertEqual(baddict3.fromkeys({"a", "b", "c"}), res)
325
326 def test_copy(self):
327 d = {1: 1, 2: 2, 3: 3}
328 self.assertIsNot(d.copy(), d)
329 self.assertEqual(d.copy(), d)
330 self.assertEqual(d.copy(), {1: 1, 2: 2, 3: 3})
331
332 copy = d.copy()
333 d[4] = 4
334 self.assertNotEqual(copy, d)
335
336 self.assertEqual({}.copy(), {})
337 self.assertRaises(TypeError, d.copy, None)
338
339 def test_copy_fuzz(self):
340 for dict_size in [10, 100, 1000, 10000, 100000]:
341 dict_size = random.randrange(
342 dict_size // 2, dict_size + dict_size // 2)
343 with self.subTest(dict_size=dict_size):
344 d = {}
345 for i in range(dict_size):
346 d[i] = i
347
348 d2 = d.copy()
349 self.assertIsNot(d2, d)
350 self.assertEqual(d, d2)
351 d2['key'] = 'value'
352 self.assertNotEqual(d, d2)
353 self.assertEqual(len(d2), len(d) + 1)
354
355 def test_copy_maintains_tracking(self):
356 class ESC[4;38;5;81mA:
357 pass
358
359 key = A()
360
361 for d in ({}, {'a': 1}, {key: 'val'}):
362 d2 = d.copy()
363 self.assertEqual(gc.is_tracked(d), gc.is_tracked(d2))
364
365 def test_copy_noncompact(self):
366 # Dicts don't compact themselves on del/pop operations.
367 # Copy will use a slow merging strategy that produces
368 # a compacted copy when roughly 33% of dict is a non-used
369 # keys-space (to optimize memory footprint).
370 # In this test we want to hit the slow/compacting
371 # branch of dict.copy() and make sure it works OK.
372 d = {k: k for k in range(1000)}
373 for k in range(950):
374 del d[k]
375 d2 = d.copy()
376 self.assertEqual(d2, d)
377
378 def test_get(self):
379 d = {}
380 self.assertIs(d.get('c'), None)
381 self.assertEqual(d.get('c', 3), 3)
382 d = {'a': 1, 'b': 2}
383 self.assertIs(d.get('c'), None)
384 self.assertEqual(d.get('c', 3), 3)
385 self.assertEqual(d.get('a'), 1)
386 self.assertEqual(d.get('a', 3), 1)
387 self.assertRaises(TypeError, d.get)
388 self.assertRaises(TypeError, d.get, None, None, None)
389
390 def test_setdefault(self):
391 # dict.setdefault()
392 d = {}
393 self.assertIs(d.setdefault('key0'), None)
394 d.setdefault('key0', [])
395 self.assertIs(d.setdefault('key0'), None)
396 d.setdefault('key', []).append(3)
397 self.assertEqual(d['key'][0], 3)
398 d.setdefault('key', []).append(4)
399 self.assertEqual(len(d['key']), 2)
400 self.assertRaises(TypeError, d.setdefault)
401
402 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
403
404 class ESC[4;38;5;81mBadHash(ESC[4;38;5;149mobject):
405 fail = False
406 def __hash__(self):
407 if self.fail:
408 raise Exc()
409 else:
410 return 42
411
412 x = BadHash()
413 d[x] = 42
414 x.fail = True
415 self.assertRaises(Exc, d.setdefault, x, [])
416
417 def test_setdefault_atomic(self):
418 # Issue #13521: setdefault() calls __hash__ and __eq__ only once.
419 class ESC[4;38;5;81mHashed(ESC[4;38;5;149mobject):
420 def __init__(self):
421 self.hash_count = 0
422 self.eq_count = 0
423 def __hash__(self):
424 self.hash_count += 1
425 return 42
426 def __eq__(self, other):
427 self.eq_count += 1
428 return id(self) == id(other)
429 hashed1 = Hashed()
430 y = {hashed1: 5}
431 hashed2 = Hashed()
432 y.setdefault(hashed2, [])
433 self.assertEqual(hashed1.hash_count, 1)
434 self.assertEqual(hashed2.hash_count, 1)
435 self.assertEqual(hashed1.eq_count + hashed2.eq_count, 1)
436
437 def test_setitem_atomic_at_resize(self):
438 class ESC[4;38;5;81mHashed(ESC[4;38;5;149mobject):
439 def __init__(self):
440 self.hash_count = 0
441 self.eq_count = 0
442 def __hash__(self):
443 self.hash_count += 1
444 return 42
445 def __eq__(self, other):
446 self.eq_count += 1
447 return id(self) == id(other)
448 hashed1 = Hashed()
449 # 5 items
450 y = {hashed1: 5, 0: 0, 1: 1, 2: 2, 3: 3}
451 hashed2 = Hashed()
452 # 6th item forces a resize
453 y[hashed2] = []
454 self.assertEqual(hashed1.hash_count, 1)
455 self.assertEqual(hashed2.hash_count, 1)
456 self.assertEqual(hashed1.eq_count + hashed2.eq_count, 1)
457
458 def test_popitem(self):
459 # dict.popitem()
460 for copymode in -1, +1:
461 # -1: b has same structure as a
462 # +1: b is a.copy()
463 for log2size in range(12):
464 size = 2**log2size
465 a = {}
466 b = {}
467 for i in range(size):
468 a[repr(i)] = i
469 if copymode < 0:
470 b[repr(i)] = i
471 if copymode > 0:
472 b = a.copy()
473 for i in range(size):
474 ka, va = ta = a.popitem()
475 self.assertEqual(va, int(ka))
476 kb, vb = tb = b.popitem()
477 self.assertEqual(vb, int(kb))
478 self.assertFalse(copymode < 0 and ta != tb)
479 self.assertFalse(a)
480 self.assertFalse(b)
481
482 d = {}
483 self.assertRaises(KeyError, d.popitem)
484
485 def test_pop(self):
486 # Tests for pop with specified key
487 d = {}
488 k, v = 'abc', 'def'
489 d[k] = v
490 self.assertRaises(KeyError, d.pop, 'ghi')
491
492 self.assertEqual(d.pop(k), v)
493 self.assertEqual(len(d), 0)
494
495 self.assertRaises(KeyError, d.pop, k)
496
497 self.assertEqual(d.pop(k, v), v)
498 d[k] = v
499 self.assertEqual(d.pop(k, 1), v)
500
501 self.assertRaises(TypeError, d.pop)
502
503 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
504
505 class ESC[4;38;5;81mBadHash(ESC[4;38;5;149mobject):
506 fail = False
507 def __hash__(self):
508 if self.fail:
509 raise Exc()
510 else:
511 return 42
512
513 x = BadHash()
514 d[x] = 42
515 x.fail = True
516 self.assertRaises(Exc, d.pop, x)
517
518 def test_mutating_iteration(self):
519 # changing dict size during iteration
520 d = {}
521 d[1] = 1
522 with self.assertRaises(RuntimeError):
523 for i in d:
524 d[i+1] = 1
525
526 def test_mutating_iteration_delete(self):
527 # change dict content during iteration
528 d = {}
529 d[0] = 0
530 with self.assertRaises(RuntimeError):
531 for i in d:
532 del d[0]
533 d[0] = 0
534
535 def test_mutating_iteration_delete_over_values(self):
536 # change dict content during iteration
537 d = {}
538 d[0] = 0
539 with self.assertRaises(RuntimeError):
540 for i in d.values():
541 del d[0]
542 d[0] = 0
543
544 def test_mutating_iteration_delete_over_items(self):
545 # change dict content during iteration
546 d = {}
547 d[0] = 0
548 with self.assertRaises(RuntimeError):
549 for i in d.items():
550 del d[0]
551 d[0] = 0
552
553 def test_mutating_lookup(self):
554 # changing dict during a lookup (issue #14417)
555 class ESC[4;38;5;81mNastyKey:
556 mutate_dict = None
557
558 def __init__(self, value):
559 self.value = value
560
561 def __hash__(self):
562 # hash collision!
563 return 1
564
565 def __eq__(self, other):
566 if NastyKey.mutate_dict:
567 mydict, key = NastyKey.mutate_dict
568 NastyKey.mutate_dict = None
569 del mydict[key]
570 return self.value == other.value
571
572 key1 = NastyKey(1)
573 key2 = NastyKey(2)
574 d = {key1: 1}
575 NastyKey.mutate_dict = (d, key1)
576 d[key2] = 2
577 self.assertEqual(d, {key2: 2})
578
579 def test_repr(self):
580 d = {}
581 self.assertEqual(repr(d), '{}')
582 d[1] = 2
583 self.assertEqual(repr(d), '{1: 2}')
584 d = {}
585 d[1] = d
586 self.assertEqual(repr(d), '{1: {...}}')
587
588 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
589
590 class ESC[4;38;5;81mBadRepr(ESC[4;38;5;149mobject):
591 def __repr__(self):
592 raise Exc()
593
594 d = {1: BadRepr()}
595 self.assertRaises(Exc, repr, d)
596
597 def test_repr_deep(self):
598 d = {}
599 for i in range(C_RECURSION_LIMIT + 1):
600 d = {1: d}
601 self.assertRaises(RecursionError, repr, d)
602
603 def test_eq(self):
604 self.assertEqual({}, {})
605 self.assertEqual({1: 2}, {1: 2})
606
607 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
608
609 class ESC[4;38;5;81mBadCmp(ESC[4;38;5;149mobject):
610 def __eq__(self, other):
611 raise Exc()
612 def __hash__(self):
613 return 1
614
615 d1 = {BadCmp(): 1}
616 d2 = {1: 1}
617
618 with self.assertRaises(Exc):
619 d1 == d2
620
621 def test_keys_contained(self):
622 self.helper_keys_contained(lambda x: x.keys())
623 self.helper_keys_contained(lambda x: x.items())
624
625 def helper_keys_contained(self, fn):
626 # Test rich comparisons against dict key views, which should behave the
627 # same as sets.
628 empty = fn(dict())
629 empty2 = fn(dict())
630 smaller = fn({1:1, 2:2})
631 larger = fn({1:1, 2:2, 3:3})
632 larger2 = fn({1:1, 2:2, 3:3})
633 larger3 = fn({4:1, 2:2, 3:3})
634
635 self.assertTrue(smaller < larger)
636 self.assertTrue(smaller <= larger)
637 self.assertTrue(larger > smaller)
638 self.assertTrue(larger >= smaller)
639
640 self.assertFalse(smaller >= larger)
641 self.assertFalse(smaller > larger)
642 self.assertFalse(larger <= smaller)
643 self.assertFalse(larger < smaller)
644
645 self.assertFalse(smaller < larger3)
646 self.assertFalse(smaller <= larger3)
647 self.assertFalse(larger3 > smaller)
648 self.assertFalse(larger3 >= smaller)
649
650 # Inequality strictness
651 self.assertTrue(larger2 >= larger)
652 self.assertTrue(larger2 <= larger)
653 self.assertFalse(larger2 > larger)
654 self.assertFalse(larger2 < larger)
655
656 self.assertTrue(larger == larger2)
657 self.assertTrue(smaller != larger)
658
659 # There is an optimization on the zero-element case.
660 self.assertTrue(empty == empty2)
661 self.assertFalse(empty != empty2)
662 self.assertFalse(empty == smaller)
663 self.assertTrue(empty != smaller)
664
665 # With the same size, an elementwise compare happens
666 self.assertTrue(larger != larger3)
667 self.assertFalse(larger == larger3)
668
669 def test_errors_in_view_containment_check(self):
670 class ESC[4;38;5;81mC:
671 def __eq__(self, other):
672 raise RuntimeError
673
674 d1 = {1: C()}
675 d2 = {1: C()}
676 with self.assertRaises(RuntimeError):
677 d1.items() == d2.items()
678 with self.assertRaises(RuntimeError):
679 d1.items() != d2.items()
680 with self.assertRaises(RuntimeError):
681 d1.items() <= d2.items()
682 with self.assertRaises(RuntimeError):
683 d1.items() >= d2.items()
684
685 d3 = {1: C(), 2: C()}
686 with self.assertRaises(RuntimeError):
687 d2.items() < d3.items()
688 with self.assertRaises(RuntimeError):
689 d3.items() > d2.items()
690
691 def test_dictview_set_operations_on_keys(self):
692 k1 = {1:1, 2:2}.keys()
693 k2 = {1:1, 2:2, 3:3}.keys()
694 k3 = {4:4}.keys()
695
696 self.assertEqual(k1 - k2, set())
697 self.assertEqual(k1 - k3, {1,2})
698 self.assertEqual(k2 - k1, {3})
699 self.assertEqual(k3 - k1, {4})
700 self.assertEqual(k1 & k2, {1,2})
701 self.assertEqual(k1 & k3, set())
702 self.assertEqual(k1 | k2, {1,2,3})
703 self.assertEqual(k1 ^ k2, {3})
704 self.assertEqual(k1 ^ k3, {1,2,4})
705
706 def test_dictview_set_operations_on_items(self):
707 k1 = {1:1, 2:2}.items()
708 k2 = {1:1, 2:2, 3:3}.items()
709 k3 = {4:4}.items()
710
711 self.assertEqual(k1 - k2, set())
712 self.assertEqual(k1 - k3, {(1,1), (2,2)})
713 self.assertEqual(k2 - k1, {(3,3)})
714 self.assertEqual(k3 - k1, {(4,4)})
715 self.assertEqual(k1 & k2, {(1,1), (2,2)})
716 self.assertEqual(k1 & k3, set())
717 self.assertEqual(k1 | k2, {(1,1), (2,2), (3,3)})
718 self.assertEqual(k1 ^ k2, {(3,3)})
719 self.assertEqual(k1 ^ k3, {(1,1), (2,2), (4,4)})
720
721 def test_items_symmetric_difference(self):
722 rr = random.randrange
723 for _ in range(100):
724 left = {x:rr(3) for x in range(20) if rr(2)}
725 right = {x:rr(3) for x in range(20) if rr(2)}
726 with self.subTest(left=left, right=right):
727 expected = set(left.items()) ^ set(right.items())
728 actual = left.items() ^ right.items()
729 self.assertEqual(actual, expected)
730
731 def test_dictview_mixed_set_operations(self):
732 # Just a few for .keys()
733 self.assertTrue({1:1}.keys() == {1})
734 self.assertTrue({1} == {1:1}.keys())
735 self.assertEqual({1:1}.keys() | {2}, {1, 2})
736 self.assertEqual({2} | {1:1}.keys(), {1, 2})
737 # And a few for .items()
738 self.assertTrue({1:1}.items() == {(1,1)})
739 self.assertTrue({(1,1)} == {1:1}.items())
740 self.assertEqual({1:1}.items() | {2}, {(1,1), 2})
741 self.assertEqual({2} | {1:1}.items(), {(1,1), 2})
742
743 def test_missing(self):
744 # Make sure dict doesn't have a __missing__ method
745 self.assertFalse(hasattr(dict, "__missing__"))
746 self.assertFalse(hasattr({}, "__missing__"))
747 # Test several cases:
748 # (D) subclass defines __missing__ method returning a value
749 # (E) subclass defines __missing__ method raising RuntimeError
750 # (F) subclass sets __missing__ instance variable (no effect)
751 # (G) subclass doesn't define __missing__ at all
752 class ESC[4;38;5;81mD(ESC[4;38;5;149mdict):
753 def __missing__(self, key):
754 return 42
755 d = D({1: 2, 3: 4})
756 self.assertEqual(d[1], 2)
757 self.assertEqual(d[3], 4)
758 self.assertNotIn(2, d)
759 self.assertNotIn(2, d.keys())
760 self.assertEqual(d[2], 42)
761
762 class ESC[4;38;5;81mE(ESC[4;38;5;149mdict):
763 def __missing__(self, key):
764 raise RuntimeError(key)
765 e = E()
766 with self.assertRaises(RuntimeError) as c:
767 e[42]
768 self.assertEqual(c.exception.args, (42,))
769
770 class ESC[4;38;5;81mF(ESC[4;38;5;149mdict):
771 def __init__(self):
772 # An instance variable __missing__ should have no effect
773 self.__missing__ = lambda key: None
774 f = F()
775 with self.assertRaises(KeyError) as c:
776 f[42]
777 self.assertEqual(c.exception.args, (42,))
778
779 class ESC[4;38;5;81mG(ESC[4;38;5;149mdict):
780 pass
781 g = G()
782 with self.assertRaises(KeyError) as c:
783 g[42]
784 self.assertEqual(c.exception.args, (42,))
785
786 def test_tuple_keyerror(self):
787 # SF #1576657
788 d = {}
789 with self.assertRaises(KeyError) as c:
790 d[(1,)]
791 self.assertEqual(c.exception.args, ((1,),))
792
793 def test_bad_key(self):
794 # Dictionary lookups should fail if __eq__() raises an exception.
795 class ESC[4;38;5;81mCustomException(ESC[4;38;5;149mException):
796 pass
797
798 class ESC[4;38;5;81mBadDictKey:
799 def __hash__(self):
800 return hash(self.__class__)
801
802 def __eq__(self, other):
803 if isinstance(other, self.__class__):
804 raise CustomException
805 return other
806
807 d = {}
808 x1 = BadDictKey()
809 x2 = BadDictKey()
810 d[x1] = 1
811 for stmt in ['d[x2] = 2',
812 'z = d[x2]',
813 'x2 in d',
814 'd.get(x2)',
815 'd.setdefault(x2, 42)',
816 'd.pop(x2)',
817 'd.update({x2: 2})']:
818 with self.assertRaises(CustomException):
819 exec(stmt, locals())
820
821 def test_resize1(self):
822 # Dict resizing bug, found by Jack Jansen in 2.2 CVS development.
823 # This version got an assert failure in debug build, infinite loop in
824 # release build. Unfortunately, provoking this kind of stuff requires
825 # a mix of inserts and deletes hitting exactly the right hash codes in
826 # exactly the right order, and I can't think of a randomized approach
827 # that would be *likely* to hit a failing case in reasonable time.
828
829 d = {}
830 for i in range(5):
831 d[i] = i
832 for i in range(5):
833 del d[i]
834 for i in range(5, 9): # i==8 was the problem
835 d[i] = i
836
837 def test_resize2(self):
838 # Another dict resizing bug (SF bug #1456209).
839 # This caused Segmentation faults or Illegal instructions.
840
841 class ESC[4;38;5;81mX(ESC[4;38;5;149mobject):
842 def __hash__(self):
843 return 5
844 def __eq__(self, other):
845 if resizing:
846 d.clear()
847 return False
848 d = {}
849 resizing = False
850 d[X()] = 1
851 d[X()] = 2
852 d[X()] = 3
853 d[X()] = 4
854 d[X()] = 5
855 # now trigger a resize
856 resizing = True
857 d[9] = 6
858
859 def test_empty_presized_dict_in_freelist(self):
860 # Bug #3537: if an empty but presized dict with a size larger
861 # than 7 was in the freelist, it triggered an assertion failure
862 with self.assertRaises(ZeroDivisionError):
863 d = {'a': 1 // 0, 'b': None, 'c': None, 'd': None, 'e': None,
864 'f': None, 'g': None, 'h': None}
865 d = {}
866
867 def test_container_iterator(self):
868 # Bug #3680: tp_traverse was not implemented for dictiter and
869 # dictview objects.
870 class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
871 pass
872 views = (dict.items, dict.values, dict.keys)
873 for v in views:
874 obj = C()
875 ref = weakref.ref(obj)
876 container = {obj: 1}
877 obj.v = v(container)
878 obj.x = iter(obj.v)
879 del obj, container
880 gc.collect()
881 self.assertIs(ref(), None, "Cycle was not collected")
882
883 def _not_tracked(self, t):
884 # Nested containers can take several collections to untrack
885 gc.collect()
886 gc.collect()
887 self.assertFalse(gc.is_tracked(t), t)
888
889 def _tracked(self, t):
890 self.assertTrue(gc.is_tracked(t), t)
891 gc.collect()
892 gc.collect()
893 self.assertTrue(gc.is_tracked(t), t)
894
895 def test_string_keys_can_track_values(self):
896 # Test that this doesn't leak.
897 for i in range(10):
898 d = {}
899 for j in range(10):
900 d[str(j)] = j
901 d["foo"] = d
902
903 @support.cpython_only
904 def test_track_literals(self):
905 # Test GC-optimization of dict literals
906 x, y, z, w = 1.5, "a", (1, None), []
907
908 self._not_tracked({})
909 self._not_tracked({x:(), y:x, z:1})
910 self._not_tracked({1: "a", "b": 2})
911 self._not_tracked({1: 2, (None, True, False, ()): int})
912 self._not_tracked({1: object()})
913
914 # Dicts with mutable elements are always tracked, even if those
915 # elements are not tracked right now.
916 self._tracked({1: []})
917 self._tracked({1: ([],)})
918 self._tracked({1: {}})
919 self._tracked({1: set()})
920
921 @support.cpython_only
922 def test_track_dynamic(self):
923 # Test GC-optimization of dynamically-created dicts
924 class ESC[4;38;5;81mMyObject(ESC[4;38;5;149mobject):
925 pass
926 x, y, z, w, o = 1.5, "a", (1, object()), [], MyObject()
927
928 d = dict()
929 self._not_tracked(d)
930 d[1] = "a"
931 self._not_tracked(d)
932 d[y] = 2
933 self._not_tracked(d)
934 d[z] = 3
935 self._not_tracked(d)
936 self._not_tracked(d.copy())
937 d[4] = w
938 self._tracked(d)
939 self._tracked(d.copy())
940 d[4] = None
941 self._not_tracked(d)
942 self._not_tracked(d.copy())
943
944 # dd isn't tracked right now, but it may mutate and therefore d
945 # which contains it must be tracked.
946 d = dict()
947 dd = dict()
948 d[1] = dd
949 self._not_tracked(dd)
950 self._tracked(d)
951 dd[1] = d
952 self._tracked(dd)
953
954 d = dict.fromkeys([x, y, z])
955 self._not_tracked(d)
956 dd = dict()
957 dd.update(d)
958 self._not_tracked(dd)
959 d = dict.fromkeys([x, y, z, o])
960 self._tracked(d)
961 dd = dict()
962 dd.update(d)
963 self._tracked(dd)
964
965 d = dict(x=x, y=y, z=z)
966 self._not_tracked(d)
967 d = dict(x=x, y=y, z=z, w=w)
968 self._tracked(d)
969 d = dict()
970 d.update(x=x, y=y, z=z)
971 self._not_tracked(d)
972 d.update(w=w)
973 self._tracked(d)
974
975 d = dict([(x, y), (z, 1)])
976 self._not_tracked(d)
977 d = dict([(x, y), (z, w)])
978 self._tracked(d)
979 d = dict()
980 d.update([(x, y), (z, 1)])
981 self._not_tracked(d)
982 d.update([(x, y), (z, w)])
983 self._tracked(d)
984
985 @support.cpython_only
986 def test_track_subtypes(self):
987 # Dict subtypes are always tracked
988 class ESC[4;38;5;81mMyDict(ESC[4;38;5;149mdict):
989 pass
990 self._tracked(MyDict())
991
992 def make_shared_key_dict(self, n):
993 class ESC[4;38;5;81mC:
994 pass
995
996 dicts = []
997 for i in range(n):
998 a = C()
999 a.x, a.y, a.z = 1, 2, 3
1000 dicts.append(a.__dict__)
1001
1002 return dicts
1003
1004 @support.cpython_only
1005 def test_splittable_setdefault(self):
1006 """split table must keep correct insertion
1007 order when attributes are adding using setdefault()"""
1008 a, b = self.make_shared_key_dict(2)
1009
1010 a['a'] = 1
1011 size_a = sys.getsizeof(a)
1012 a['b'] = 2
1013 b.setdefault('b', 2)
1014 size_b = sys.getsizeof(b)
1015 b['a'] = 1
1016
1017 self.assertEqual(list(a), ['x', 'y', 'z', 'a', 'b'])
1018 self.assertEqual(list(b), ['x', 'y', 'z', 'b', 'a'])
1019
1020 @support.cpython_only
1021 def test_splittable_del(self):
1022 """split table must be combined when del d[k]"""
1023 a, b = self.make_shared_key_dict(2)
1024
1025 orig_size = sys.getsizeof(a)
1026
1027 del a['y'] # split table is combined
1028 with self.assertRaises(KeyError):
1029 del a['y']
1030
1031 self.assertEqual(list(a), ['x', 'z'])
1032 self.assertEqual(list(b), ['x', 'y', 'z'])
1033
1034 # Two dicts have different insertion order.
1035 a['y'] = 42
1036 self.assertEqual(list(a), ['x', 'z', 'y'])
1037 self.assertEqual(list(b), ['x', 'y', 'z'])
1038
1039 @support.cpython_only
1040 def test_splittable_pop(self):
1041 a, b = self.make_shared_key_dict(2)
1042
1043 a.pop('y')
1044 with self.assertRaises(KeyError):
1045 a.pop('y')
1046
1047 self.assertEqual(list(a), ['x', 'z'])
1048 self.assertEqual(list(b), ['x', 'y', 'z'])
1049
1050 # Two dicts have different insertion order.
1051 a['y'] = 42
1052 self.assertEqual(list(a), ['x', 'z', 'y'])
1053 self.assertEqual(list(b), ['x', 'y', 'z'])
1054
1055 @support.cpython_only
1056 def test_splittable_pop_pending(self):
1057 """pop a pending key in a split table should not crash"""
1058 a, b = self.make_shared_key_dict(2)
1059
1060 a['a'] = 4
1061 with self.assertRaises(KeyError):
1062 b.pop('a')
1063
1064 @support.cpython_only
1065 def test_splittable_popitem(self):
1066 """split table must be combined when d.popitem()"""
1067 a, b = self.make_shared_key_dict(2)
1068
1069 orig_size = sys.getsizeof(a)
1070
1071 item = a.popitem() # split table is combined
1072 self.assertEqual(item, ('z', 3))
1073 with self.assertRaises(KeyError):
1074 del a['z']
1075
1076 self.assertGreater(sys.getsizeof(a), orig_size)
1077 self.assertEqual(list(a), ['x', 'y'])
1078 self.assertEqual(list(b), ['x', 'y', 'z'])
1079
1080 @support.cpython_only
1081 def test_splittable_update(self):
1082 """dict.update(other) must preserve order in other."""
1083 class ESC[4;38;5;81mC:
1084 def __init__(self, order):
1085 if order:
1086 self.a, self.b, self.c = 1, 2, 3
1087 else:
1088 self.c, self.b, self.a = 1, 2, 3
1089 o = C(True)
1090 o = C(False) # o.__dict__ has reversed order.
1091 self.assertEqual(list(o.__dict__), ["c", "b", "a"])
1092
1093 d = {}
1094 d.update(o.__dict__)
1095 self.assertEqual(list(d), ["c", "b", "a"])
1096
1097 @support.cpython_only
1098 def test_splittable_to_generic_combinedtable(self):
1099 """split table must be correctly resized and converted to generic combined table"""
1100 class ESC[4;38;5;81mC:
1101 pass
1102
1103 a = C()
1104 a.x = 1
1105 d = a.__dict__
1106 before_resize = sys.getsizeof(d)
1107 d[2] = 2 # split table is resized to a generic combined table
1108
1109 self.assertGreater(sys.getsizeof(d), before_resize)
1110 self.assertEqual(list(d), ['x', 2])
1111
1112 def test_iterator_pickling(self):
1113 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1114 data = {1:"a", 2:"b", 3:"c"}
1115 it = iter(data)
1116 d = pickle.dumps(it, proto)
1117 it = pickle.loads(d)
1118 self.assertEqual(list(it), list(data))
1119
1120 it = pickle.loads(d)
1121 try:
1122 drop = next(it)
1123 except StopIteration:
1124 continue
1125 d = pickle.dumps(it, proto)
1126 it = pickle.loads(d)
1127 del data[drop]
1128 self.assertEqual(list(it), list(data))
1129
1130 def test_itemiterator_pickling(self):
1131 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1132 data = {1:"a", 2:"b", 3:"c"}
1133 # dictviews aren't picklable, only their iterators
1134 itorg = iter(data.items())
1135 d = pickle.dumps(itorg, proto)
1136 it = pickle.loads(d)
1137 # note that the type of the unpickled iterator
1138 # is not necessarily the same as the original. It is
1139 # merely an object supporting the iterator protocol, yielding
1140 # the same objects as the original one.
1141 # self.assertEqual(type(itorg), type(it))
1142 self.assertIsInstance(it, collections.abc.Iterator)
1143 self.assertEqual(dict(it), data)
1144
1145 it = pickle.loads(d)
1146 drop = next(it)
1147 d = pickle.dumps(it, proto)
1148 it = pickle.loads(d)
1149 del data[drop[0]]
1150 self.assertEqual(dict(it), data)
1151
1152 def test_valuesiterator_pickling(self):
1153 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1154 data = {1:"a", 2:"b", 3:"c"}
1155 # data.values() isn't picklable, only its iterator
1156 it = iter(data.values())
1157 d = pickle.dumps(it, proto)
1158 it = pickle.loads(d)
1159 self.assertEqual(list(it), list(data.values()))
1160
1161 it = pickle.loads(d)
1162 drop = next(it)
1163 d = pickle.dumps(it, proto)
1164 it = pickle.loads(d)
1165 values = list(it) + [drop]
1166 self.assertEqual(sorted(values), sorted(list(data.values())))
1167
1168 def test_reverseiterator_pickling(self):
1169 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1170 data = {1:"a", 2:"b", 3:"c"}
1171 it = reversed(data)
1172 d = pickle.dumps(it, proto)
1173 it = pickle.loads(d)
1174 self.assertEqual(list(it), list(reversed(data)))
1175
1176 it = pickle.loads(d)
1177 try:
1178 drop = next(it)
1179 except StopIteration:
1180 continue
1181 d = pickle.dumps(it, proto)
1182 it = pickle.loads(d)
1183 del data[drop]
1184 self.assertEqual(list(it), list(reversed(data)))
1185
1186 def test_reverseitemiterator_pickling(self):
1187 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1188 data = {1:"a", 2:"b", 3:"c"}
1189 # dictviews aren't picklable, only their iterators
1190 itorg = reversed(data.items())
1191 d = pickle.dumps(itorg, proto)
1192 it = pickle.loads(d)
1193 # note that the type of the unpickled iterator
1194 # is not necessarily the same as the original. It is
1195 # merely an object supporting the iterator protocol, yielding
1196 # the same objects as the original one.
1197 # self.assertEqual(type(itorg), type(it))
1198 self.assertIsInstance(it, collections.abc.Iterator)
1199 self.assertEqual(dict(it), data)
1200
1201 it = pickle.loads(d)
1202 drop = next(it)
1203 d = pickle.dumps(it, proto)
1204 it = pickle.loads(d)
1205 del data[drop[0]]
1206 self.assertEqual(dict(it), data)
1207
1208 def test_reversevaluesiterator_pickling(self):
1209 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1210 data = {1:"a", 2:"b", 3:"c"}
1211 # data.values() isn't picklable, only its iterator
1212 it = reversed(data.values())
1213 d = pickle.dumps(it, proto)
1214 it = pickle.loads(d)
1215 self.assertEqual(list(it), list(reversed(data.values())))
1216
1217 it = pickle.loads(d)
1218 drop = next(it)
1219 d = pickle.dumps(it, proto)
1220 it = pickle.loads(d)
1221 values = list(it) + [drop]
1222 self.assertEqual(sorted(values), sorted(data.values()))
1223
1224 def test_instance_dict_getattr_str_subclass(self):
1225 class ESC[4;38;5;81mFoo:
1226 def __init__(self, msg):
1227 self.msg = msg
1228 f = Foo('123')
1229 class ESC[4;38;5;81m_str(ESC[4;38;5;149mstr):
1230 pass
1231 self.assertEqual(f.msg, getattr(f, _str('msg')))
1232 self.assertEqual(f.msg, f.__dict__[_str('msg')])
1233
1234 def test_object_set_item_single_instance_non_str_key(self):
1235 class ESC[4;38;5;81mFoo: pass
1236 f = Foo()
1237 f.__dict__[1] = 1
1238 f.a = 'a'
1239 self.assertEqual(f.__dict__, {1:1, 'a':'a'})
1240
1241 def check_reentrant_insertion(self, mutate):
1242 # This object will trigger mutation of the dict when replaced
1243 # by another value. Note this relies on refcounting: the test
1244 # won't achieve its purpose on fully-GCed Python implementations.
1245 class ESC[4;38;5;81mMutating:
1246 def __del__(self):
1247 mutate(d)
1248
1249 d = {k: Mutating() for k in 'abcdefghijklmnopqr'}
1250 for k in list(d):
1251 d[k] = k
1252
1253 def test_reentrant_insertion(self):
1254 # Reentrant insertion shouldn't crash (see issue #22653)
1255 def mutate(d):
1256 d['b'] = 5
1257 self.check_reentrant_insertion(mutate)
1258
1259 def mutate(d):
1260 d.update(self.__dict__)
1261 d.clear()
1262 self.check_reentrant_insertion(mutate)
1263
1264 def mutate(d):
1265 while d:
1266 d.popitem()
1267 self.check_reentrant_insertion(mutate)
1268
1269 def test_merge_and_mutate(self):
1270 class ESC[4;38;5;81mX:
1271 def __hash__(self):
1272 return 0
1273
1274 def __eq__(self, o):
1275 other.clear()
1276 return False
1277
1278 l = [(i,0) for i in range(1, 1337)]
1279 other = dict(l)
1280 other[X()] = 0
1281 d = {X(): 0, 1: 1}
1282 self.assertRaises(RuntimeError, d.update, other)
1283
1284 def test_free_after_iterating(self):
1285 support.check_free_after_iterating(self, iter, dict)
1286 support.check_free_after_iterating(self, lambda d: iter(d.keys()), dict)
1287 support.check_free_after_iterating(self, lambda d: iter(d.values()), dict)
1288 support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)
1289
1290 def test_equal_operator_modifying_operand(self):
1291 # test fix for seg fault reported in bpo-27945 part 3.
1292 class ESC[4;38;5;81mX():
1293 def __del__(self):
1294 dict_b.clear()
1295
1296 def __eq__(self, other):
1297 dict_a.clear()
1298 return True
1299
1300 def __hash__(self):
1301 return 13
1302
1303 dict_a = {X(): 0}
1304 dict_b = {X(): X()}
1305 self.assertTrue(dict_a == dict_b)
1306
1307 # test fix for seg fault reported in bpo-38588 part 1.
1308 class ESC[4;38;5;81mY:
1309 def __eq__(self, other):
1310 dict_d.clear()
1311 return True
1312
1313 dict_c = {0: Y()}
1314 dict_d = {0: set()}
1315 self.assertTrue(dict_c == dict_d)
1316
1317 def test_fromkeys_operator_modifying_dict_operand(self):
1318 # test fix for seg fault reported in issue 27945 part 4a.
1319 class ESC[4;38;5;81mX(ESC[4;38;5;149mint):
1320 def __hash__(self):
1321 return 13
1322
1323 def __eq__(self, other):
1324 if len(d) > 1:
1325 d.clear()
1326 return False
1327
1328 d = {} # this is required to exist so that d can be constructed!
1329 d = {X(1): 1, X(2): 2}
1330 try:
1331 dict.fromkeys(d) # shouldn't crash
1332 except RuntimeError: # implementation defined
1333 pass
1334
1335 def test_fromkeys_operator_modifying_set_operand(self):
1336 # test fix for seg fault reported in issue 27945 part 4b.
1337 class ESC[4;38;5;81mX(ESC[4;38;5;149mint):
1338 def __hash__(self):
1339 return 13
1340
1341 def __eq__(self, other):
1342 if len(d) > 1:
1343 d.clear()
1344 return False
1345
1346 d = {} # this is required to exist so that d can be constructed!
1347 d = {X(1), X(2)}
1348 try:
1349 dict.fromkeys(d) # shouldn't crash
1350 except RuntimeError: # implementation defined
1351 pass
1352
1353 def test_dictitems_contains_use_after_free(self):
1354 class ESC[4;38;5;81mX:
1355 def __eq__(self, other):
1356 d.clear()
1357 return NotImplemented
1358
1359 d = {0: set()}
1360 (0, X()) in d.items()
1361
1362 def test_dict_contain_use_after_free(self):
1363 # bpo-40489
1364 class ESC[4;38;5;81mS(ESC[4;38;5;149mstr):
1365 def __eq__(self, other):
1366 d.clear()
1367 return NotImplemented
1368
1369 def __hash__(self):
1370 return hash('test')
1371
1372 d = {S(): 'value'}
1373 self.assertFalse('test' in d)
1374
1375 def test_init_use_after_free(self):
1376 class ESC[4;38;5;81mX:
1377 def __hash__(self):
1378 pair[:] = []
1379 return 13
1380
1381 pair = [X(), 123]
1382 dict([pair])
1383
1384 def test_oob_indexing_dictiter_iternextitem(self):
1385 class ESC[4;38;5;81mX(ESC[4;38;5;149mint):
1386 def __del__(self):
1387 d.clear()
1388
1389 d = {i: X(i) for i in range(8)}
1390
1391 def iter_and_mutate():
1392 for result in d.items():
1393 if result[0] == 2:
1394 d[2] = None # free d[2] --> X(2).__del__ was called
1395
1396 self.assertRaises(RuntimeError, iter_and_mutate)
1397
1398 def test_reversed(self):
1399 d = {"a": 1, "b": 2, "foo": 0, "c": 3, "d": 4}
1400 del d["foo"]
1401 r = reversed(d)
1402 self.assertEqual(list(r), list('dcba'))
1403 self.assertRaises(StopIteration, next, r)
1404
1405 def test_reverse_iterator_for_empty_dict(self):
1406 # bpo-38525: reversed iterator should work properly
1407
1408 # empty dict is directly used for reference count test
1409 self.assertEqual(list(reversed({})), [])
1410 self.assertEqual(list(reversed({}.items())), [])
1411 self.assertEqual(list(reversed({}.values())), [])
1412 self.assertEqual(list(reversed({}.keys())), [])
1413
1414 # dict() and {} don't trigger the same code path
1415 self.assertEqual(list(reversed(dict())), [])
1416 self.assertEqual(list(reversed(dict().items())), [])
1417 self.assertEqual(list(reversed(dict().values())), [])
1418 self.assertEqual(list(reversed(dict().keys())), [])
1419
1420 def test_reverse_iterator_for_shared_shared_dicts(self):
1421 class ESC[4;38;5;81mA:
1422 def __init__(self, x, y):
1423 if x: self.x = x
1424 if y: self.y = y
1425
1426 self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x'])
1427 self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
1428 self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])
1429
1430 def test_dict_copy_order(self):
1431 # bpo-34320
1432 od = collections.OrderedDict([('a', 1), ('b', 2)])
1433 od.move_to_end('a')
1434 expected = list(od.items())
1435
1436 copy = dict(od)
1437 self.assertEqual(list(copy.items()), expected)
1438
1439 # dict subclass doesn't override __iter__
1440 class ESC[4;38;5;81mCustomDict(ESC[4;38;5;149mdict):
1441 pass
1442
1443 pairs = [('a', 1), ('b', 2), ('c', 3)]
1444
1445 d = CustomDict(pairs)
1446 self.assertEqual(pairs, list(dict(d).items()))
1447
1448 class ESC[4;38;5;81mCustomReversedDict(ESC[4;38;5;149mdict):
1449 def keys(self):
1450 return reversed(list(dict.keys(self)))
1451
1452 __iter__ = keys
1453
1454 def items(self):
1455 return reversed(dict.items(self))
1456
1457 d = CustomReversedDict(pairs)
1458 self.assertEqual(pairs[::-1], list(dict(d).items()))
1459
1460 @support.cpython_only
1461 def test_dict_items_result_gc(self):
1462 # bpo-42536: dict.items's tuple-reuse speed trick breaks the GC's
1463 # assumptions about what can be untracked. Make sure we re-track result
1464 # tuples whenever we reuse them.
1465 it = iter({None: []}.items())
1466 gc.collect()
1467 # That GC collection probably untracked the recycled internal result
1468 # tuple, which is initialized to (None, None). Make sure it's re-tracked
1469 # when it's mutated and returned from __next__:
1470 self.assertTrue(gc.is_tracked(next(it)))
1471
1472 @support.cpython_only
1473 def test_dict_items_result_gc_reversed(self):
1474 # Same as test_dict_items_result_gc above, but reversed.
1475 it = reversed({None: []}.items())
1476 gc.collect()
1477 self.assertTrue(gc.is_tracked(next(it)))
1478
1479 def test_str_nonstr(self):
1480 # cpython uses a different lookup function if the dict only contains
1481 # `str` keys. Make sure the unoptimized path is used when a non-`str`
1482 # key appears.
1483
1484 class ESC[4;38;5;81mStrSub(ESC[4;38;5;149mstr):
1485 pass
1486
1487 eq_count = 0
1488 # This class compares equal to the string 'key3'
1489 class ESC[4;38;5;81mKey3:
1490 def __hash__(self):
1491 return hash('key3')
1492
1493 def __eq__(self, other):
1494 nonlocal eq_count
1495 if isinstance(other, Key3) or isinstance(other, str) and other == 'key3':
1496 eq_count += 1
1497 return True
1498 return False
1499
1500 key3_1 = StrSub('key3')
1501 key3_2 = Key3()
1502 key3_3 = Key3()
1503
1504 dicts = []
1505
1506 # Create dicts of the form `{'key1': 42, 'key2': 43, key3: 44}` in a
1507 # bunch of different ways. In all cases, `key3` is not of type `str`.
1508 # `key3_1` is a `str` subclass and `key3_2` is a completely unrelated
1509 # type.
1510 for key3 in (key3_1, key3_2):
1511 # A literal
1512 dicts.append({'key1': 42, 'key2': 43, key3: 44})
1513
1514 # key3 inserted via `dict.__setitem__`
1515 d = {'key1': 42, 'key2': 43}
1516 d[key3] = 44
1517 dicts.append(d)
1518
1519 # key3 inserted via `dict.setdefault`
1520 d = {'key1': 42, 'key2': 43}
1521 self.assertEqual(d.setdefault(key3, 44), 44)
1522 dicts.append(d)
1523
1524 # key3 inserted via `dict.update`
1525 d = {'key1': 42, 'key2': 43}
1526 d.update({key3: 44})
1527 dicts.append(d)
1528
1529 # key3 inserted via `dict.__ior__`
1530 d = {'key1': 42, 'key2': 43}
1531 d |= {key3: 44}
1532 dicts.append(d)
1533
1534 # `dict(iterable)`
1535 def make_pairs():
1536 yield ('key1', 42)
1537 yield ('key2', 43)
1538 yield (key3, 44)
1539 d = dict(make_pairs())
1540 dicts.append(d)
1541
1542 # `dict.copy`
1543 d = d.copy()
1544 dicts.append(d)
1545
1546 # dict comprehension
1547 d = {key: 42 + i for i,key in enumerate(['key1', 'key2', key3])}
1548 dicts.append(d)
1549
1550 for d in dicts:
1551 with self.subTest(d=d):
1552 self.assertEqual(d.get('key1'), 42)
1553
1554 # Try to make an object that is of type `str` and is equal to
1555 # `'key1'`, but (at least on cpython) is a different object.
1556 noninterned_key1 = 'ke'
1557 noninterned_key1 += 'y1'
1558 if support.check_impl_detail(cpython=True):
1559 # suppress a SyntaxWarning
1560 interned_key1 = 'key1'
1561 self.assertFalse(noninterned_key1 is interned_key1)
1562 self.assertEqual(d.get(noninterned_key1), 42)
1563
1564 self.assertEqual(d.get('key3'), 44)
1565 self.assertEqual(d.get(key3_1), 44)
1566 self.assertEqual(d.get(key3_2), 44)
1567
1568 # `key3_3` itself is definitely not a dict key, so make sure
1569 # that `__eq__` gets called.
1570 #
1571 # Note that this might not hold for `key3_1` and `key3_2`
1572 # because they might be the same object as one of the dict keys,
1573 # in which case implementations are allowed to skip the call to
1574 # `__eq__`.
1575 eq_count = 0
1576 self.assertEqual(d.get(key3_3), 44)
1577 self.assertGreaterEqual(eq_count, 1)
1578
1579
1580 class ESC[4;38;5;81mCAPITest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1581
1582 # Test _PyDict_GetItem_KnownHash()
1583 @support.cpython_only
1584 def test_getitem_knownhash(self):
1585 _testcapi = import_helper.import_module('_testcapi')
1586 dict_getitem_knownhash = _testcapi.dict_getitem_knownhash
1587
1588 d = {'x': 1, 'y': 2, 'z': 3}
1589 self.assertEqual(dict_getitem_knownhash(d, 'x', hash('x')), 1)
1590 self.assertEqual(dict_getitem_knownhash(d, 'y', hash('y')), 2)
1591 self.assertEqual(dict_getitem_knownhash(d, 'z', hash('z')), 3)
1592
1593 # not a dict
1594 self.assertRaises(SystemError, dict_getitem_knownhash, [], 1, hash(1))
1595 # key does not exist
1596 self.assertRaises(KeyError, dict_getitem_knownhash, {}, 1, hash(1))
1597
1598 class ESC[4;38;5;81mExc(ESC[4;38;5;149mException): pass
1599 class ESC[4;38;5;81mBadEq:
1600 def __eq__(self, other):
1601 raise Exc
1602 def __hash__(self):
1603 return 7
1604
1605 k1, k2 = BadEq(), BadEq()
1606 d = {k1: 1}
1607 self.assertEqual(dict_getitem_knownhash(d, k1, hash(k1)), 1)
1608 self.assertRaises(Exc, dict_getitem_knownhash, d, k2, hash(k2))
1609
1610
1611 from test import mapping_tests
1612
1613 class ESC[4;38;5;81mGeneralMappingTests(ESC[4;38;5;149mmapping_testsESC[4;38;5;149m.ESC[4;38;5;149mBasicTestMappingProtocol):
1614 type2test = dict
1615
1616 class ESC[4;38;5;81mDict(ESC[4;38;5;149mdict):
1617 pass
1618
1619 class ESC[4;38;5;81mSubclassMappingTests(ESC[4;38;5;149mmapping_testsESC[4;38;5;149m.ESC[4;38;5;149mBasicTestMappingProtocol):
1620 type2test = Dict
1621
1622
1623 if __name__ == "__main__":
1624 unittest.main()