@@ -1392,14 +1392,19 @@ def __hash__(self):
13921392 self .assertEqual (hash (view ), hash (mapping ))
13931393
13941394 def test_richcompare (self ):
1395- mp1 = self .mappingproxy ({'a' : 1 })
1395+ dt1 = {'a' : 1 }
1396+ mp1 = self .mappingproxy (dt1 )
13961397 mp1_2 = self .mappingproxy ({'a' : 1 })
13971398 mp2 = self .mappingproxy ({'a' : 2 })
13981399
13991400 self .assertTrue (mp1 == mp1_2 )
14001401 self .assertFalse (mp1 != mp1_2 )
14011402 self .assertFalse (mp1 == mp2 )
14021403 self .assertTrue (mp1 != mp2 )
1404+ self .assertTrue (mp1 == dt1 )
1405+ self .assertTrue (mp1_2 == dt1 )
1406+ self .assertFalse (mp2 == dt1 )
1407+ self .assertTrue (mp2 != dt1 )
14031408
14041409 msg = "not supported between instances of 'mappingproxy' and 'mappingproxy'"
14051410
@@ -1411,6 +1416,67 @@ def test_richcompare(self):
14111416 mp2 >= mp2
14121417 with self .assertRaisesRegex (TypeError , msg ):
14131418 mp1_2 <= mp1
1419+ with self .assertRaisesRegex (
1420+ TypeError ,
1421+ "not supported between instances of 'mappingproxy' and 'dict'" ,
1422+ ):
1423+ mp1_2 <= dt1
1424+
1425+ def test_richcompare_mapping (self ):
1426+ class CustomMapping (collections .abc .Mapping ):
1427+ def __init__ (self , data ):
1428+ self ._data = data
1429+
1430+ def __getitem__ (self , key ):
1431+ return self ._data [key ]
1432+
1433+ def __iter__ (self ):
1434+ return iter (self ._data )
1435+
1436+ def __len__ (self ):
1437+ return len (self ._data )
1438+
1439+ def __contains__ (self , item ):
1440+ return item in self ._data
1441+
1442+ dt1 = {'a' : 1 }
1443+ mp1 = self .mappingproxy (CustomMapping (dt1 ))
1444+ mp1_2 = self .mappingproxy (dt1 )
1445+ mp2 = self .mappingproxy ({'a' : 2 })
1446+
1447+ self .assertTrue (mp1 == mp1_2 )
1448+ self .assertTrue (mp1 == dt1 )
1449+ self .assertTrue (mp1_2 == dt1 )
1450+ self .assertTrue (dt1 == mp1 )
1451+ self .assertTrue (dt1 == mp1_2 )
1452+ self .assertFalse (mp2 == dt1 )
1453+ self .assertFalse (dt1 == mp2 )
1454+ self .assertFalse (mp1 != dt1 )
1455+ self .assertFalse (dt1 != mp1 )
1456+
1457+ class Evil :
1458+ def __eq__ (self , other ):
1459+ return other
1460+
1461+ leaked = mp1 == Evil ()
1462+ self .assertIs (type (leaked ), dict )
1463+
1464+ def test_richcompare_evil (self ):
1465+ # https://github.com/python/cpython/issues/152405
1466+ class Evil :
1467+ def __eq__ (self , other ):
1468+ return other
1469+
1470+ # exposes the internals of `MappingProxyType` via richcompare:
1471+ leaked = vars (list ) == Evil ()
1472+ self .assertIs (type (leaked ), dict )
1473+ name = "__mappingproxy_crash_probe__"
1474+ leaked [name ] = lambda self : "probe"
1475+ self .assertIn (name , leaked )
1476+ self .assertNotHasAttr (list , name ) # it used to return `True`
1477+ del leaked [name ]
1478+ self .assertNotIn (name , leaked )
1479+ self .assertNotHasAttr (list , name ) # it used to crash
14141480
14151481
14161482class ClassCreationTests (unittest .TestCase ):
0 commit comments