@@ -1298,6 +1298,36 @@ def check_weak_destroy_and_mutate_while_iterating(self, dict, testcontext):
12981298 dict .clear ()
12991299 self .assertEqual (len (dict ), 0 )
13001300
1301+ def check_weak_del_and_len_while_iterating (self , dict , testcontext ):
1302+ # Check that len() works when both iterating and removing keys
1303+ # explicitly through various means (.pop(), .clear()...), while
1304+ # implicit mutation is deferred because an iterator is alive.
1305+ # (each call to testcontext() should schedule one item for removal
1306+ # for this test to work properly)
1307+ o = Object (123456 )
1308+ with testcontext ():
1309+ n = len (dict )
1310+ dict .popitem ()
1311+ self .assertEqual (len (dict ), n - 1 )
1312+ dict [o ] = o
1313+ self .assertEqual (len (dict ), n )
1314+ with testcontext ():
1315+ self .assertEqual (len (dict ), n - 1 )
1316+ dict .pop (next (dict .keys ()))
1317+ self .assertEqual (len (dict ), n - 2 )
1318+ with testcontext ():
1319+ self .assertEqual (len (dict ), n - 3 )
1320+ del dict [next (dict .keys ())]
1321+ self .assertEqual (len (dict ), n - 4 )
1322+ with testcontext ():
1323+ self .assertEqual (len (dict ), n - 5 )
1324+ dict .popitem ()
1325+ self .assertEqual (len (dict ), n - 6 )
1326+ with testcontext ():
1327+ dict .clear ()
1328+ self .assertEqual (len (dict ), 0 )
1329+ self .assertEqual (len (dict ), 0 )
1330+
13011331 def test_weak_keys_destroy_while_iterating (self ):
13021332 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
13031333 dict , objects = self .make_weak_keyed_dict ()
@@ -1319,6 +1349,10 @@ def testcontext():
13191349 it = None # should commit all removals
13201350 gc .collect ()
13211351 self .check_weak_destroy_and_mutate_while_iterating (dict , testcontext )
1352+ # Issue #21173: len() fragile when keys are both implicitly and
1353+ # explicitly removed.
1354+ dict , objects = self .make_weak_keyed_dict ()
1355+ self .check_weak_del_and_len_while_iterating (dict , testcontext )
13221356
13231357 def test_weak_values_destroy_while_iterating (self ):
13241358 # Issue #7105: iterators shouldn't crash when a key is implicitly removed
@@ -1342,6 +1376,8 @@ def testcontext():
13421376 it = None # should commit all removals
13431377 gc .collect ()
13441378 self .check_weak_destroy_and_mutate_while_iterating (dict , testcontext )
1379+ dict , objects = self .make_weak_valued_dict ()
1380+ self .check_weak_del_and_len_while_iterating (dict , testcontext )
13451381
13461382 def test_make_weak_keyed_dict_from_dict (self ):
13471383 o = Object (3 )
0 commit comments