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