Skip to content

Commit c62076e

Browse files
CPython developersyouknowone
authored andcommitted
Update test_dict from CPython 3.10.5
1 parent b1c29aa commit c62076e

File tree

2 files changed

+236
-8
lines changed

2 files changed

+236
-8
lines changed

Lib/test/test_dict.py

Lines changed: 232 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import collections
22
import collections.abc
3-
# import gc // XXX RustPython
3+
import gc
44
import pickle
55
import random
66
import string
@@ -37,6 +37,38 @@ def test_literal_constructor(self):
3737
dictliteral = '{' + ', '.join(formatted_items) + '}'
3838
self.assertEqual(eval(dictliteral), dict(items))
3939

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+
4072
def test_bool(self):
4173
self.assertIs(not {}, True)
4274
self.assertTrue({1: 2})
@@ -73,6 +105,26 @@ def test_items(self):
73105
self.assertRaises(TypeError, d.items, None)
74106
self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
75107

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+
76128
def test_contains(self):
77129
d = {}
78130
self.assertNotIn('a', d)
@@ -668,6 +720,16 @@ def test_dictview_set_operations_on_items(self):
668720
self.assertEqual(k1 ^ k2, {(3,3)})
669721
self.assertEqual(k1 ^ k3, {(1,1), (2,2), (4,4)})
670722

723+
def test_items_symmetric_difference(self):
724+
rr = random.randrange
725+
for _ in range(100):
726+
left = {x:rr(3) for x in range(20) if rr(2)}
727+
right = {x:rr(3) for x in range(20) if rr(2)}
728+
with self.subTest(left=left, right=right):
729+
expected = set(left.items()) ^ set(right.items())
730+
actual = left.items() ^ right.items()
731+
self.assertEqual(actual, expected)
732+
671733
def test_dictview_mixed_set_operations(self):
672734
# Just a few for .keys()
673735
self.assertTrue({1:1}.keys() == {1})
@@ -994,7 +1056,7 @@ def test_splittable_pop(self):
9941056

9951057
@support.cpython_only
9961058
def test_splittable_pop_pending(self):
997-
"""pop a pending key in a splitted table should not crash"""
1059+
"""pop a pending key in a split table should not crash"""
9981060
a, b = self.make_shared_key_dict(2)
9991061

10001062
a['a'] = 4
@@ -1230,7 +1292,7 @@ def test_free_after_iterating(self):
12301292
# TODO: RUSTPYTHON
12311293
@unittest.expectedFailure
12321294
def test_equal_operator_modifying_operand(self):
1233-
# test fix for seg fault reported in issue 27945 part 3.
1295+
# test fix for seg fault reported in bpo-27945 part 3.
12341296
class X():
12351297
def __del__(self):
12361298
dict_b.clear()
@@ -1246,6 +1308,16 @@ def __hash__(self):
12461308
dict_b = {X(): X()}
12471309
self.assertTrue(dict_a == dict_b)
12481310

1311+
# test fix for seg fault reported in bpo-38588 part 1.
1312+
class Y:
1313+
def __eq__(self, other):
1314+
dict_d.clear()
1315+
return True
1316+
1317+
dict_c = {0: Y()}
1318+
dict_d = {0: set()}
1319+
self.assertTrue(dict_c == dict_d)
1320+
12491321
def test_fromkeys_operator_modifying_dict_operand(self):
12501322
# test fix for seg fault reported in issue 27945 part 4a.
12511323
class X(int):
@@ -1291,6 +1363,19 @@ def __eq__(self, other):
12911363
d = {0: set()}
12921364
(0, X()) in d.items()
12931365

1366+
def test_dict_contain_use_after_free(self):
1367+
# bpo-40489
1368+
class S(str):
1369+
def __eq__(self, other):
1370+
d.clear()
1371+
return NotImplemented
1372+
1373+
def __hash__(self):
1374+
return hash('test')
1375+
1376+
d = {S(): 'value'}
1377+
self.assertFalse('test' in d)
1378+
12941379
def test_init_use_after_free(self):
12951380
class X:
12961381
def __hash__(self):
@@ -1321,6 +1406,31 @@ def test_reversed(self):
13211406
self.assertEqual(list(r), list('dcba'))
13221407
self.assertRaises(StopIteration, next, r)
13231408

1409+
def test_reverse_iterator_for_empty_dict(self):
1410+
# bpo-38525: reversed iterator should work properly
1411+
1412+
# empty dict is directly used for reference count test
1413+
self.assertEqual(list(reversed({})), [])
1414+
self.assertEqual(list(reversed({}.items())), [])
1415+
self.assertEqual(list(reversed({}.values())), [])
1416+
self.assertEqual(list(reversed({}.keys())), [])
1417+
1418+
# dict() and {} don't trigger the same code path
1419+
self.assertEqual(list(reversed(dict())), [])
1420+
self.assertEqual(list(reversed(dict().items())), [])
1421+
self.assertEqual(list(reversed(dict().values())), [])
1422+
self.assertEqual(list(reversed(dict().keys())), [])
1423+
1424+
def test_reverse_iterator_for_shared_shared_dicts(self):
1425+
class A:
1426+
def __init__(self, x, y):
1427+
if x: self.x = x
1428+
if y: self.y = y
1429+
1430+
self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x'])
1431+
self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
1432+
self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])
1433+
13241434
def test_dict_copy_order(self):
13251435
# bpo-34320
13261436
od = collections.OrderedDict([('a', 1), ('b', 2)])
@@ -1351,6 +1461,125 @@ def items(self):
13511461
d = CustomReversedDict(pairs)
13521462
self.assertEqual(pairs[::-1], list(dict(d).items()))
13531463

1464+
@support.cpython_only
1465+
def test_dict_items_result_gc(self):
1466+
# bpo-42536: dict.items's tuple-reuse speed trick breaks the GC's
1467+
# assumptions about what can be untracked. Make sure we re-track result
1468+
# tuples whenever we reuse them.
1469+
it = iter({None: []}.items())
1470+
gc.collect()
1471+
# That GC collection probably untracked the recycled internal result
1472+
# tuple, which is initialized to (None, None). Make sure it's re-tracked
1473+
# when it's mutated and returned from __next__:
1474+
self.assertTrue(gc.is_tracked(next(it)))
1475+
1476+
@support.cpython_only
1477+
def test_dict_items_result_gc_reversed(self):
1478+
# Same as test_dict_items_result_gc above, but reversed.
1479+
it = reversed({None: []}.items())
1480+
gc.collect()
1481+
self.assertTrue(gc.is_tracked(next(it)))
1482+
1483+
def test_str_nonstr(self):
1484+
# cpython uses a different lookup function if the dict only contains
1485+
# `str` keys. Make sure the unoptimized path is used when a non-`str`
1486+
# key appears.
1487+
1488+
class StrSub(str):
1489+
pass
1490+
1491+
eq_count = 0
1492+
# This class compares equal to the string 'key3'
1493+
class Key3:
1494+
def __hash__(self):
1495+
return hash('key3')
1496+
1497+
def __eq__(self, other):
1498+
nonlocal eq_count
1499+
if isinstance(other, Key3) or isinstance(other, str) and other == 'key3':
1500+
eq_count += 1
1501+
return True
1502+
return False
1503+
1504+
key3_1 = StrSub('key3')
1505+
key3_2 = Key3()
1506+
key3_3 = Key3()
1507+
1508+
dicts = []
1509+
1510+
# Create dicts of the form `{'key1': 42, 'key2': 43, key3: 44}` in a
1511+
# bunch of different ways. In all cases, `key3` is not of type `str`.
1512+
# `key3_1` is a `str` subclass and `key3_2` is a completely unrelated
1513+
# type.
1514+
for key3 in (key3_1, key3_2):
1515+
# A literal
1516+
dicts.append({'key1': 42, 'key2': 43, key3: 44})
1517+
1518+
# key3 inserted via `dict.__setitem__`
1519+
d = {'key1': 42, 'key2': 43}
1520+
d[key3] = 44
1521+
dicts.append(d)
1522+
1523+
# key3 inserted via `dict.setdefault`
1524+
d = {'key1': 42, 'key2': 43}
1525+
self.assertEqual(d.setdefault(key3, 44), 44)
1526+
dicts.append(d)
1527+
1528+
# key3 inserted via `dict.update`
1529+
d = {'key1': 42, 'key2': 43}
1530+
d.update({key3: 44})
1531+
dicts.append(d)
1532+
1533+
# key3 inserted via `dict.__ior__`
1534+
d = {'key1': 42, 'key2': 43}
1535+
d |= {key3: 44}
1536+
dicts.append(d)
1537+
1538+
# `dict(iterable)`
1539+
def make_pairs():
1540+
yield ('key1', 42)
1541+
yield ('key2', 43)
1542+
yield (key3, 44)
1543+
d = dict(make_pairs())
1544+
dicts.append(d)
1545+
1546+
# `dict.copy`
1547+
d = d.copy()
1548+
dicts.append(d)
1549+
1550+
# dict comprehension
1551+
d = {key: 42 + i for i,key in enumerate(['key1', 'key2', key3])}
1552+
dicts.append(d)
1553+
1554+
for d in dicts:
1555+
with self.subTest(d=d):
1556+
self.assertEqual(d.get('key1'), 42)
1557+
1558+
# Try to make an object that is of type `str` and is equal to
1559+
# `'key1'`, but (at least on cpython) is a different object.
1560+
noninterned_key1 = 'ke'
1561+
noninterned_key1 += 'y1'
1562+
if support.check_impl_detail(cpython=True):
1563+
# suppress a SyntaxWarning
1564+
interned_key1 = 'key1'
1565+
self.assertFalse(noninterned_key1 is interned_key1)
1566+
self.assertEqual(d.get(noninterned_key1), 42)
1567+
1568+
self.assertEqual(d.get('key3'), 44)
1569+
self.assertEqual(d.get(key3_1), 44)
1570+
self.assertEqual(d.get(key3_2), 44)
1571+
1572+
# `key3_3` itself is definitely not a dict key, so make sure
1573+
# that `__eq__` gets called.
1574+
#
1575+
# Note that this might not hold for `key3_1` and `key3_2`
1576+
# because they might be the same object as one of the dict keys,
1577+
# in which case implementations are allowed to skip the call to
1578+
# `__eq__`.
1579+
eq_count = 0
1580+
self.assertEqual(d.get(key3_3), 44)
1581+
self.assertGreaterEqual(eq_count, 1)
1582+
13541583

13551584
class CAPITest(unittest.TestCase):
13561585

Lib/test/test_userdict.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def test_all(self):
3030
self.assertEqual(collections.UserDict(one=1, two=2), d2)
3131
# item sequence constructor
3232
self.assertEqual(collections.UserDict([('one',1), ('two',2)]), d2)
33-
with self.assertWarnsRegex(DeprecationWarning, "'dict'"):
34-
self.assertEqual(collections.UserDict(dict=[('one',1), ('two',2)]), d2)
33+
self.assertEqual(collections.UserDict(dict=[('one',1), ('two',2)]),
34+
{'dict': [('one', 1), ('two', 2)]})
3535
# both together
3636
self.assertEqual(collections.UserDict([('one',1), ('two',2)], two=3, three=5), d3)
3737

@@ -149,9 +149,8 @@ def test_init(self):
149149
[('dict', 42)])
150150
self.assertEqual(list(collections.UserDict({}, dict=None).items()),
151151
[('dict', None)])
152-
with self.assertWarnsRegex(DeprecationWarning, "'dict'"):
153-
self.assertEqual(list(collections.UserDict(dict={'a': 42}).items()),
154-
[('a', 42)])
152+
self.assertEqual(list(collections.UserDict(dict={'a': 42}).items()),
153+
[('dict', {'a': 42})])
155154
self.assertRaises(TypeError, collections.UserDict, 42)
156155
self.assertRaises(TypeError, collections.UserDict, (), ())
157156
self.assertRaises(TypeError, collections.UserDict.__init__)

0 commit comments

Comments
 (0)