@@ -54,6 +54,10 @@ def sleep_and_print(t, msg):
5454 sys .stdout .flush ()
5555
5656
57+ def _dummy_object_fn (_ ):
58+ return object ()
59+
60+
5761class MyObject (object ):
5862 def my_method (self ):
5963 pass
@@ -396,6 +400,34 @@ def test_duplicate_futures(self):
396400 completed = [f for f in futures .as_completed ([future1 ,future1 ])]
397401 self .assertEqual (len (completed ), 1 )
398402
403+ def test_free_reference_yielded_future (self ):
404+ # Issue #14406: Generator should not keep reference
405+ # for finished futures.
406+ futures_list = [Future () for _ in range (8 )]
407+ futures_list .append (create_future (state = CANCELLED_AND_NOTIFIED ))
408+ futures_list .append (create_future (state = SUCCESSFUL_FUTURE ))
409+
410+ with self .assertRaises (futures .TimeoutError ):
411+ for future in futures .as_completed (futures_list , timeout = 0 ):
412+ futures_list .remove (future )
413+ self .assertEqual (sys .getrefcount (future ), 2 )
414+
415+ futures_list [0 ].set_result ("test" )
416+ for future in futures .as_completed (futures_list ):
417+ futures_list .remove (future )
418+ self .assertEqual (sys .getrefcount (future ), 2 )
419+ if futures_list :
420+ futures_list [0 ].set_result ("test" )
421+
422+ def test_correct_timeout_exception_msg (self ):
423+ futures_list = [CANCELLED_AND_NOTIFIED_FUTURE , PENDING_FUTURE ,
424+ RUNNING_FUTURE , SUCCESSFUL_FUTURE ]
425+
426+ with self .assertRaises (futures .TimeoutError ) as cm :
427+ list (futures .as_completed (futures_list , timeout = 0 ))
428+
429+ self .assertEqual (str (cm .exception ), '2 (of 4) futures unfinished' )
430+
399431
400432class ThreadPoolAsCompletedTests (ThreadPoolMixin , AsCompletedTests , BaseTestCase ):
401433 pass
@@ -421,6 +453,10 @@ def test_map(self):
421453 list (self .executor .map (pow , range (10 ), range (10 ))),
422454 list (map (pow , range (10 ), range (10 ))))
423455
456+ self .assertEqual (
457+ list (self .executor .map (pow , range (10 ), range (10 ), chunksize = 3 )),
458+ list (map (pow , range (10 ), range (10 ))))
459+
424460 def test_map_exception (self ):
425461 i = self .executor .map (divmod , [1 , 1 , 1 , 1 ], [2 , 3 , 0 , 5 ])
426462 self .assertEqual (i .__next__ (), (0 , 1 ))
@@ -471,6 +507,12 @@ def test_max_workers_negative(self):
471507 "than 0" ):
472508 self .executor_type (max_workers = number )
473509
510+ def test_free_reference (self ):
511+ # Issue #14406: Result iterator should not keep reference
512+ # for finished futures.
513+ for result_object in self .executor .map (_dummy_object_fn , range (10 )):
514+ self .assertEqual (sys .getrefcount (result_object ), 2 )
515+
474516
475517class ThreadPoolExecutorTest (ThreadPoolMixin , ExecutorTest , BaseTestCase ):
476518 def test_map_submits_without_iteration (self ):
0 commit comments