1import collections 2import collections.abc 3import gc 4import pickle 5import random 6import string 7import sys 8import unittest 9import weakref 10from test import support 11from test.support import import_helper 12 13 14class DictTest(unittest.TestCase): 15 16 def test_invalid_keyword_arguments(self): 17 class Custom(dict): 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 Dict(dict): 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 BadEq(object): 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 Exc(Exception): pass 171 172 class BadHash(object): 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 SimpleUserDict: 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 Exc(Exception): pass 216 217 d.clear() 218 class FailingUserDict: 219 def keys(self): 220 raise Exc 221 self.assertRaises(Exc, d.update, FailingUserDict()) 222 223 class FailingUserDict: 224 def keys(self): 225 class BogonIter: 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 FailingUserDict: 241 def keys(self): 242 class BogonIter: 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 badseq(object): 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 dictlike(dict): 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 mydict(dict): 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 Exc(Exception): pass 293 294 class baddict1(dict): 295 def __init__(self): 296 raise Exc() 297 298 self.assertRaises(Exc, baddict1.fromkeys, [1]) 299 300 class BadSeq(object): 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 baddict2(dict): 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 baddict3(dict): 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 A: 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 Exc(Exception): pass 403 404 class BadHash(object): 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 Hashed(object): 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 Hashed(object): 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 Exc(Exception): pass 504 505 class BadHash(object): 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 NastyKey: 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 Exc(Exception): pass 589 590 class BadRepr(object): 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(sys.getrecursionlimit() + 100): 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 Exc(Exception): pass 608 609 class BadCmp(object): 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 C: 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 D(dict): 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 E(dict): 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 F(dict): 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 G(dict): 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 CustomException(Exception): 796 pass 797 798 class BadDictKey: 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 X(object): 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 C(object): 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 MyObject(object): 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 MyDict(dict): 989 pass 990 self._tracked(MyDict()) 991 992 def make_shared_key_dict(self, n): 993 class C: 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 C: 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 def test_iterator_pickling(self): 1098 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1099 data = {1:"a", 2:"b", 3:"c"} 1100 it = iter(data) 1101 d = pickle.dumps(it, proto) 1102 it = pickle.loads(d) 1103 self.assertEqual(list(it), list(data)) 1104 1105 it = pickle.loads(d) 1106 try: 1107 drop = next(it) 1108 except StopIteration: 1109 continue 1110 d = pickle.dumps(it, proto) 1111 it = pickle.loads(d) 1112 del data[drop] 1113 self.assertEqual(list(it), list(data)) 1114 1115 def test_itemiterator_pickling(self): 1116 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1117 data = {1:"a", 2:"b", 3:"c"} 1118 # dictviews aren't picklable, only their iterators 1119 itorg = iter(data.items()) 1120 d = pickle.dumps(itorg, proto) 1121 it = pickle.loads(d) 1122 # note that the type of the unpickled iterator 1123 # is not necessarily the same as the original. It is 1124 # merely an object supporting the iterator protocol, yielding 1125 # the same objects as the original one. 1126 # self.assertEqual(type(itorg), type(it)) 1127 self.assertIsInstance(it, collections.abc.Iterator) 1128 self.assertEqual(dict(it), data) 1129 1130 it = pickle.loads(d) 1131 drop = next(it) 1132 d = pickle.dumps(it, proto) 1133 it = pickle.loads(d) 1134 del data[drop[0]] 1135 self.assertEqual(dict(it), data) 1136 1137 def test_valuesiterator_pickling(self): 1138 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1139 data = {1:"a", 2:"b", 3:"c"} 1140 # data.values() isn't picklable, only its iterator 1141 it = iter(data.values()) 1142 d = pickle.dumps(it, proto) 1143 it = pickle.loads(d) 1144 self.assertEqual(list(it), list(data.values())) 1145 1146 it = pickle.loads(d) 1147 drop = next(it) 1148 d = pickle.dumps(it, proto) 1149 it = pickle.loads(d) 1150 values = list(it) + [drop] 1151 self.assertEqual(sorted(values), sorted(list(data.values()))) 1152 1153 def test_reverseiterator_pickling(self): 1154 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1155 data = {1:"a", 2:"b", 3:"c"} 1156 it = reversed(data) 1157 d = pickle.dumps(it, proto) 1158 it = pickle.loads(d) 1159 self.assertEqual(list(it), list(reversed(data))) 1160 1161 it = pickle.loads(d) 1162 try: 1163 drop = next(it) 1164 except StopIteration: 1165 continue 1166 d = pickle.dumps(it, proto) 1167 it = pickle.loads(d) 1168 del data[drop] 1169 self.assertEqual(list(it), list(reversed(data))) 1170 1171 def test_reverseitemiterator_pickling(self): 1172 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1173 data = {1:"a", 2:"b", 3:"c"} 1174 # dictviews aren't picklable, only their iterators 1175 itorg = reversed(data.items()) 1176 d = pickle.dumps(itorg, proto) 1177 it = pickle.loads(d) 1178 # note that the type of the unpickled iterator 1179 # is not necessarily the same as the original. It is 1180 # merely an object supporting the iterator protocol, yielding 1181 # the same objects as the original one. 1182 # self.assertEqual(type(itorg), type(it)) 1183 self.assertIsInstance(it, collections.abc.Iterator) 1184 self.assertEqual(dict(it), data) 1185 1186 it = pickle.loads(d) 1187 drop = next(it) 1188 d = pickle.dumps(it, proto) 1189 it = pickle.loads(d) 1190 del data[drop[0]] 1191 self.assertEqual(dict(it), data) 1192 1193 def test_reversevaluesiterator_pickling(self): 1194 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1195 data = {1:"a", 2:"b", 3:"c"} 1196 # data.values() isn't picklable, only its iterator 1197 it = reversed(data.values()) 1198 d = pickle.dumps(it, proto) 1199 it = pickle.loads(d) 1200 self.assertEqual(list(it), list(reversed(data.values()))) 1201 1202 it = pickle.loads(d) 1203 drop = next(it) 1204 d = pickle.dumps(it, proto) 1205 it = pickle.loads(d) 1206 values = list(it) + [drop] 1207 self.assertEqual(sorted(values), sorted(data.values())) 1208 1209 def test_instance_dict_getattr_str_subclass(self): 1210 class Foo: 1211 def __init__(self, msg): 1212 self.msg = msg 1213 f = Foo('123') 1214 class _str(str): 1215 pass 1216 self.assertEqual(f.msg, getattr(f, _str('msg'))) 1217 self.assertEqual(f.msg, f.__dict__[_str('msg')]) 1218 1219 def test_object_set_item_single_instance_non_str_key(self): 1220 class Foo: pass 1221 f = Foo() 1222 f.__dict__[1] = 1 1223 f.a = 'a' 1224 self.assertEqual(f.__dict__, {1:1, 'a':'a'}) 1225 1226 def check_reentrant_insertion(self, mutate): 1227 # This object will trigger mutation of the dict when replaced 1228 # by another value. Note this relies on refcounting: the test 1229 # won't achieve its purpose on fully-GCed Python implementations. 1230 class Mutating: 1231 def __del__(self): 1232 mutate(d) 1233 1234 d = {k: Mutating() for k in 'abcdefghijklmnopqr'} 1235 for k in list(d): 1236 d[k] = k 1237 1238 def test_reentrant_insertion(self): 1239 # Reentrant insertion shouldn't crash (see issue #22653) 1240 def mutate(d): 1241 d['b'] = 5 1242 self.check_reentrant_insertion(mutate) 1243 1244 def mutate(d): 1245 d.update(self.__dict__) 1246 d.clear() 1247 self.check_reentrant_insertion(mutate) 1248 1249 def mutate(d): 1250 while d: 1251 d.popitem() 1252 self.check_reentrant_insertion(mutate) 1253 1254 def test_merge_and_mutate(self): 1255 class X: 1256 def __hash__(self): 1257 return 0 1258 1259 def __eq__(self, o): 1260 other.clear() 1261 return False 1262 1263 l = [(i,0) for i in range(1, 1337)] 1264 other = dict(l) 1265 other[X()] = 0 1266 d = {X(): 0, 1: 1} 1267 self.assertRaises(RuntimeError, d.update, other) 1268 1269 def test_free_after_iterating(self): 1270 support.check_free_after_iterating(self, iter, dict) 1271 support.check_free_after_iterating(self, lambda d: iter(d.keys()), dict) 1272 support.check_free_after_iterating(self, lambda d: iter(d.values()), dict) 1273 support.check_free_after_iterating(self, lambda d: iter(d.items()), dict) 1274 1275 def test_equal_operator_modifying_operand(self): 1276 # test fix for seg fault reported in bpo-27945 part 3. 1277 class X(): 1278 def __del__(self): 1279 dict_b.clear() 1280 1281 def __eq__(self, other): 1282 dict_a.clear() 1283 return True 1284 1285 def __hash__(self): 1286 return 13 1287 1288 dict_a = {X(): 0} 1289 dict_b = {X(): X()} 1290 self.assertTrue(dict_a == dict_b) 1291 1292 # test fix for seg fault reported in bpo-38588 part 1. 1293 class Y: 1294 def __eq__(self, other): 1295 dict_d.clear() 1296 return True 1297 1298 dict_c = {0: Y()} 1299 dict_d = {0: set()} 1300 self.assertTrue(dict_c == dict_d) 1301 1302 def test_fromkeys_operator_modifying_dict_operand(self): 1303 # test fix for seg fault reported in issue 27945 part 4a. 1304 class X(int): 1305 def __hash__(self): 1306 return 13 1307 1308 def __eq__(self, other): 1309 if len(d) > 1: 1310 d.clear() 1311 return False 1312 1313 d = {} # this is required to exist so that d can be constructed! 1314 d = {X(1): 1, X(2): 2} 1315 try: 1316 dict.fromkeys(d) # shouldn't crash 1317 except RuntimeError: # implementation defined 1318 pass 1319 1320 def test_fromkeys_operator_modifying_set_operand(self): 1321 # test fix for seg fault reported in issue 27945 part 4b. 1322 class X(int): 1323 def __hash__(self): 1324 return 13 1325 1326 def __eq__(self, other): 1327 if len(d) > 1: 1328 d.clear() 1329 return False 1330 1331 d = {} # this is required to exist so that d can be constructed! 1332 d = {X(1), X(2)} 1333 try: 1334 dict.fromkeys(d) # shouldn't crash 1335 except RuntimeError: # implementation defined 1336 pass 1337 1338 def test_dictitems_contains_use_after_free(self): 1339 class X: 1340 def __eq__(self, other): 1341 d.clear() 1342 return NotImplemented 1343 1344 d = {0: set()} 1345 (0, X()) in d.items() 1346 1347 def test_dict_contain_use_after_free(self): 1348 # bpo-40489 1349 class S(str): 1350 def __eq__(self, other): 1351 d.clear() 1352 return NotImplemented 1353 1354 def __hash__(self): 1355 return hash('test') 1356 1357 d = {S(): 'value'} 1358 self.assertFalse('test' in d) 1359 1360 def test_init_use_after_free(self): 1361 class X: 1362 def __hash__(self): 1363 pair[:] = [] 1364 return 13 1365 1366 pair = [X(), 123] 1367 dict([pair]) 1368 1369 def test_oob_indexing_dictiter_iternextitem(self): 1370 class X(int): 1371 def __del__(self): 1372 d.clear() 1373 1374 d = {i: X(i) for i in range(8)} 1375 1376 def iter_and_mutate(): 1377 for result in d.items(): 1378 if result[0] == 2: 1379 d[2] = None # free d[2] --> X(2).__del__ was called 1380 1381 self.assertRaises(RuntimeError, iter_and_mutate) 1382 1383 def test_reversed(self): 1384 d = {"a": 1, "b": 2, "foo": 0, "c": 3, "d": 4} 1385 del d["foo"] 1386 r = reversed(d) 1387 self.assertEqual(list(r), list('dcba')) 1388 self.assertRaises(StopIteration, next, r) 1389 1390 def test_reverse_iterator_for_empty_dict(self): 1391 # bpo-38525: reversed iterator should work properly 1392 1393 # empty dict is directly used for reference count test 1394 self.assertEqual(list(reversed({})), []) 1395 self.assertEqual(list(reversed({}.items())), []) 1396 self.assertEqual(list(reversed({}.values())), []) 1397 self.assertEqual(list(reversed({}.keys())), []) 1398 1399 # dict() and {} don't trigger the same code path 1400 self.assertEqual(list(reversed(dict())), []) 1401 self.assertEqual(list(reversed(dict().items())), []) 1402 self.assertEqual(list(reversed(dict().values())), []) 1403 self.assertEqual(list(reversed(dict().keys())), []) 1404 1405 def test_reverse_iterator_for_shared_shared_dicts(self): 1406 class A: 1407 def __init__(self, x, y): 1408 if x: self.x = x 1409 if y: self.y = y 1410 1411 self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x']) 1412 self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x']) 1413 self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y']) 1414 1415 def test_dict_copy_order(self): 1416 # bpo-34320 1417 od = collections.OrderedDict([('a', 1), ('b', 2)]) 1418 od.move_to_end('a') 1419 expected = list(od.items()) 1420 1421 copy = dict(od) 1422 self.assertEqual(list(copy.items()), expected) 1423 1424 # dict subclass doesn't override __iter__ 1425 class CustomDict(dict): 1426 pass 1427 1428 pairs = [('a', 1), ('b', 2), ('c', 3)] 1429 1430 d = CustomDict(pairs) 1431 self.assertEqual(pairs, list(dict(d).items())) 1432 1433 class CustomReversedDict(dict): 1434 def keys(self): 1435 return reversed(list(dict.keys(self))) 1436 1437 __iter__ = keys 1438 1439 def items(self): 1440 return reversed(dict.items(self)) 1441 1442 d = CustomReversedDict(pairs) 1443 self.assertEqual(pairs[::-1], list(dict(d).items())) 1444 1445 @support.cpython_only 1446 def test_dict_items_result_gc(self): 1447 # bpo-42536: dict.items's tuple-reuse speed trick breaks the GC's 1448 # assumptions about what can be untracked. Make sure we re-track result 1449 # tuples whenever we reuse them. 1450 it = iter({None: []}.items()) 1451 gc.collect() 1452 # That GC collection probably untracked the recycled internal result 1453 # tuple, which is initialized to (None, None). Make sure it's re-tracked 1454 # when it's mutated and returned from __next__: 1455 self.assertTrue(gc.is_tracked(next(it))) 1456 1457 @support.cpython_only 1458 def test_dict_items_result_gc_reversed(self): 1459 # Same as test_dict_items_result_gc above, but reversed. 1460 it = reversed({None: []}.items()) 1461 gc.collect() 1462 self.assertTrue(gc.is_tracked(next(it))) 1463 1464 def test_str_nonstr(self): 1465 # cpython uses a different lookup function if the dict only contains 1466 # `str` keys. Make sure the unoptimized path is used when a non-`str` 1467 # key appears. 1468 1469 class StrSub(str): 1470 pass 1471 1472 eq_count = 0 1473 # This class compares equal to the string 'key3' 1474 class Key3: 1475 def __hash__(self): 1476 return hash('key3') 1477 1478 def __eq__(self, other): 1479 nonlocal eq_count 1480 if isinstance(other, Key3) or isinstance(other, str) and other == 'key3': 1481 eq_count += 1 1482 return True 1483 return False 1484 1485 key3_1 = StrSub('key3') 1486 key3_2 = Key3() 1487 key3_3 = Key3() 1488 1489 dicts = [] 1490 1491 # Create dicts of the form `{'key1': 42, 'key2': 43, key3: 44}` in a 1492 # bunch of different ways. In all cases, `key3` is not of type `str`. 1493 # `key3_1` is a `str` subclass and `key3_2` is a completely unrelated 1494 # type. 1495 for key3 in (key3_1, key3_2): 1496 # A literal 1497 dicts.append({'key1': 42, 'key2': 43, key3: 44}) 1498 1499 # key3 inserted via `dict.__setitem__` 1500 d = {'key1': 42, 'key2': 43} 1501 d[key3] = 44 1502 dicts.append(d) 1503 1504 # key3 inserted via `dict.setdefault` 1505 d = {'key1': 42, 'key2': 43} 1506 self.assertEqual(d.setdefault(key3, 44), 44) 1507 dicts.append(d) 1508 1509 # key3 inserted via `dict.update` 1510 d = {'key1': 42, 'key2': 43} 1511 d.update({key3: 44}) 1512 dicts.append(d) 1513 1514 # key3 inserted via `dict.__ior__` 1515 d = {'key1': 42, 'key2': 43} 1516 d |= {key3: 44} 1517 dicts.append(d) 1518 1519 # `dict(iterable)` 1520 def make_pairs(): 1521 yield ('key1', 42) 1522 yield ('key2', 43) 1523 yield (key3, 44) 1524 d = dict(make_pairs()) 1525 dicts.append(d) 1526 1527 # `dict.copy` 1528 d = d.copy() 1529 dicts.append(d) 1530 1531 # dict comprehension 1532 d = {key: 42 + i for i,key in enumerate(['key1', 'key2', key3])} 1533 dicts.append(d) 1534 1535 for d in dicts: 1536 with self.subTest(d=d): 1537 self.assertEqual(d.get('key1'), 42) 1538 1539 # Try to make an object that is of type `str` and is equal to 1540 # `'key1'`, but (at least on cpython) is a different object. 1541 noninterned_key1 = 'ke' 1542 noninterned_key1 += 'y1' 1543 if support.check_impl_detail(cpython=True): 1544 # suppress a SyntaxWarning 1545 interned_key1 = 'key1' 1546 self.assertFalse(noninterned_key1 is interned_key1) 1547 self.assertEqual(d.get(noninterned_key1), 42) 1548 1549 self.assertEqual(d.get('key3'), 44) 1550 self.assertEqual(d.get(key3_1), 44) 1551 self.assertEqual(d.get(key3_2), 44) 1552 1553 # `key3_3` itself is definitely not a dict key, so make sure 1554 # that `__eq__` gets called. 1555 # 1556 # Note that this might not hold for `key3_1` and `key3_2` 1557 # because they might be the same object as one of the dict keys, 1558 # in which case implementations are allowed to skip the call to 1559 # `__eq__`. 1560 eq_count = 0 1561 self.assertEqual(d.get(key3_3), 44) 1562 self.assertGreaterEqual(eq_count, 1) 1563 1564 1565class CAPITest(unittest.TestCase): 1566 1567 # Test _PyDict_GetItem_KnownHash() 1568 @support.cpython_only 1569 def test_getitem_knownhash(self): 1570 _testcapi = import_helper.import_module('_testcapi') 1571 dict_getitem_knownhash = _testcapi.dict_getitem_knownhash 1572 1573 d = {'x': 1, 'y': 2, 'z': 3} 1574 self.assertEqual(dict_getitem_knownhash(d, 'x', hash('x')), 1) 1575 self.assertEqual(dict_getitem_knownhash(d, 'y', hash('y')), 2) 1576 self.assertEqual(dict_getitem_knownhash(d, 'z', hash('z')), 3) 1577 1578 # not a dict 1579 self.assertRaises(SystemError, dict_getitem_knownhash, [], 1, hash(1)) 1580 # key does not exist 1581 self.assertRaises(KeyError, dict_getitem_knownhash, {}, 1, hash(1)) 1582 1583 class Exc(Exception): pass 1584 class BadEq: 1585 def __eq__(self, other): 1586 raise Exc 1587 def __hash__(self): 1588 return 7 1589 1590 k1, k2 = BadEq(), BadEq() 1591 d = {k1: 1} 1592 self.assertEqual(dict_getitem_knownhash(d, k1, hash(k1)), 1) 1593 self.assertRaises(Exc, dict_getitem_knownhash, d, k2, hash(k2)) 1594 1595 1596from test import mapping_tests 1597 1598class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): 1599 type2test = dict 1600 1601class Dict(dict): 1602 pass 1603 1604class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol): 1605 type2test = Dict 1606 1607 1608if __name__ == "__main__": 1609 unittest.main() 1610