Skip to content

Commit c87a2d0

Browse files
committed
Address review
1 parent c581cc3 commit c87a2d0

2 files changed

Lines changed: 48 additions & 65 deletions

File tree

Lib/test/test_types.py

Lines changed: 45 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,40 +1391,62 @@ def __hash__(self):
13911391
view = self.mappingproxy(mapping)
13921392
self.assertEqual(hash(view), hash(mapping))
13931393

1394-
def test_richcompare(self):
1395-
dt1 = {'a': 1}
1396-
mp1 = self.mappingproxy(dt1)
1397-
mp1_2 = self.mappingproxy({'a': 1})
1398-
mp2 = self.mappingproxy({'a': 2})
1399-
1400-
self.assertTrue(mp1 == mp1_2)
1401-
self.assertFalse(mp1 != mp1_2)
1394+
def check_richcompare(self, mapping_type):
1395+
data1 = mapping_type({'a': 1})
1396+
data2 = mapping_type({'a': 2})
1397+
mp1 = self.mappingproxy(data1)
1398+
copy1 = self.mappingproxy(data1)
1399+
mp2 = self.mappingproxy(data2)
1400+
1401+
self.assertTrue(mp1 == data1)
1402+
self.assertTrue(data1 == mp1)
1403+
self.assertTrue(copy1 == data1)
1404+
1405+
self.assertTrue(mp1 == copy1)
1406+
self.assertFalse(mp1 != copy1)
1407+
14021408
self.assertFalse(mp1 == mp2)
14031409
self.assertTrue(mp1 != mp2)
1404-
self.assertTrue(mp1 == dt1)
1405-
self.assertTrue(dt1 == mp1)
1406-
self.assertTrue(mp1_2 == dt1)
1407-
self.assertFalse(mp2 == dt1)
1408-
self.assertTrue(mp2 != dt1)
1409-
self.assertTrue(dt1 != mp2)
1410+
self.assertFalse(mp2 == data1)
1411+
self.assertTrue(mp2 != data1)
1412+
self.assertTrue(data1 != mp2)
14101413

14111414
msg = "not supported between instances of 'mappingproxy' and 'mappingproxy'"
1412-
14131415
with self.assertRaisesRegex(TypeError, msg):
14141416
mp1 > mp2
14151417
with self.assertRaisesRegex(TypeError, msg):
1416-
mp1 < mp1_2
1418+
mp1 < copy1
14171419
with self.assertRaisesRegex(TypeError, msg):
14181420
mp2 >= mp2
14191421
with self.assertRaisesRegex(TypeError, msg):
1420-
mp1_2 <= mp1
1422+
copy1 <= mp1
1423+
1424+
if mapping_type.__module__ == 'collections':
1425+
mapping_name = f'{mapping_type.__module__}.{mapping_type.__name__}'
1426+
else:
1427+
mapping_name = mapping_type.__name__
14211428
with self.assertRaisesRegex(
14221429
TypeError,
1423-
"not supported between instances of 'mappingproxy' and 'dict'",
1430+
f"not supported between instances of 'mappingproxy' and '{mapping_name}'",
14241431
):
1425-
mp1_2 <= dt1
1432+
copy1 <= data1
1433+
1434+
class Evil:
1435+
def __eq__(self, other):
1436+
return other
1437+
1438+
result = (mp1 == Evil())
1439+
self.assertIs(type(result), mapping_type)
1440+
if mapping_type == dict:
1441+
# Evil.__eq__() gets a copy of the mapping
1442+
self.assertEqual(result, data1)
1443+
else:
1444+
self.assertIs(result, data1)
1445+
1446+
def test_richcompare_dict(self):
1447+
self.check_richcompare(dict)
14261448

1427-
def test_richcompare_mapping(self):
1449+
def test_richcompare_custom_mapping(self):
14281450
class CustomMapping(collections.abc.Mapping):
14291451
def __init__(self, data):
14301452
self._data = data
@@ -1441,31 +1463,7 @@ def __len__(self):
14411463
def __contains__(self, item):
14421464
return item in self._data
14431465

1444-
dt1 = {'a': 1}
1445-
mp1 = self.mappingproxy(CustomMapping(dt1))
1446-
mp1_2 = self.mappingproxy(dt1)
1447-
mp2 = self.mappingproxy({'a': 2})
1448-
1449-
self.assertTrue(mp1 == mp1)
1450-
self.assertTrue(mp1 == mp1_2)
1451-
self.assertTrue(mp1 == dt1)
1452-
self.assertTrue(mp1_2 == dt1)
1453-
self.assertTrue(dt1 == mp1)
1454-
self.assertTrue(dt1 == mp1_2)
1455-
self.assertFalse(mp1 != mp1)
1456-
self.assertFalse(mp2 == dt1)
1457-
self.assertFalse(dt1 == mp2)
1458-
self.assertFalse(mp1 != dt1)
1459-
self.assertFalse(dt1 != mp1)
1460-
1461-
class Evil:
1462-
def __eq__(self, other):
1463-
return other
1464-
1465-
leaked = mp1 == Evil()
1466-
self.assertIs(type(leaked), CustomMapping)
1467-
self.assertEqual(leaked, mp1)
1468-
self.assertEqual(leaked, dt1)
1466+
self.check_richcompare(CustomMapping)
14691467

14701468
class CustomMapping2(CustomMapping):
14711469
def __eq__(self, other):
@@ -1474,28 +1472,12 @@ def __eq__(self, other):
14741472
and self._data == other._data
14751473
)
14761474

1475+
mp1 = self.mappingproxy(CustomMapping({'a': 1}))
14771476
self.assertEqual(mp1, CustomMapping2({'a': 1}))
14781477
self.assertNotEqual(CustomMapping2({'a': 1}), mp1)
14791478

14801479
def test_richcompare_odict(self):
1481-
od1 = OrderedDict(x=1, y=2)
1482-
od1_2 = OrderedDict(x=1, y=2)
1483-
od2 = OrderedDict(y=2, x=1)
1484-
self.assertNotEqual(od1, od2)
1485-
self.assertEqual(od1, od1_2)
1486-
1487-
self.assertEqual(self.mappingproxy(od1), self.mappingproxy(od1_2))
1488-
self.assertEqual(self.mappingproxy(od1), od1_2)
1489-
self.assertNotEqual(self.mappingproxy(od1), self.mappingproxy(od2))
1490-
self.assertNotEqual(self.mappingproxy(od1), od2)
1491-
self.assertEqual(
1492-
self.mappingproxy(od1),
1493-
self.mappingproxy({'x': 1, 'y': 2}),
1494-
)
1495-
self.assertEqual(
1496-
self.mappingproxy(od1),
1497-
{'x': 1, 'y': 2},
1498-
)
1480+
self.check_richcompare(OrderedDict)
14991481

15001482
def test_richcompare_evil(self):
15011483
# https://github.com/python/cpython/issues/152405

Objects/descrobject.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,8 +1236,9 @@ mappingproxy_richcompare(PyObject *self, PyObject *w, int op)
12361236
mappingproxyobject *v = (mappingproxyobject *)self;
12371237
// We have to guard the mutable `dict` instances, because it can
12381238
// otherwise mutate the type's `__dict__` entries and cause crashes.
1239-
// But, do not create copies on known types for memory optimization.
1240-
// See gh-152405 on the details.
1239+
// But, do not create copies on known types like `OrderedDict`
1240+
// or immutable types like `frozendict`
1241+
// for memory optimization. See gh-152405 for the details.
12411242
if (
12421243
PyDict_CheckExact(v->mapping) &&
12431244
!(PyAnyDict_CheckExact(w) || PyODict_CheckExact(w))

0 commit comments

Comments
 (0)