@@ -926,6 +926,74 @@ def test_run_forever_pre_stopped(self):
926926 self .loop .run_forever ()
927927 self .loop ._selector .select .assert_called_once_with (0 )
928928
929+ async def leave_unfinalized_asyncgen (self ):
930+ # Create an async generator, iterate it partially, and leave it
931+ # to be garbage collected.
932+ # Used in async generator finalization tests.
933+ # Depends on implementation details of garbage collector. Changes
934+ # in gc may break this function.
935+ status = {'started' : False ,
936+ 'stopped' : False ,
937+ 'finalized' : False }
938+
939+ async def agen ():
940+ status ['started' ] = True
941+ try :
942+ for item in ['ZERO' , 'ONE' , 'TWO' , 'THREE' , 'FOUR' ]:
943+ yield item
944+ finally :
945+ status ['finalized' ] = True
946+
947+ ag = agen ()
948+ ai = ag .__aiter__ ()
949+
950+ async def iter_one ():
951+ try :
952+ item = await ai .__anext__ ()
953+ except StopAsyncIteration :
954+ return
955+ if item == 'THREE' :
956+ status ['stopped' ] = True
957+ return
958+ asyncio .create_task (iter_one ())
959+
960+ asyncio .create_task (iter_one ())
961+ return status
962+
963+ def test_asyncgen_finalization_by_gc (self ):
964+ # Async generators should be finalized when garbage collected.
965+ self .loop ._process_events = mock .Mock ()
966+ self .loop ._write_to_self = mock .Mock ()
967+ with support .disable_gc ():
968+ status = self .loop .run_until_complete (self .leave_unfinalized_asyncgen ())
969+ while not status ['stopped' ]:
970+ test_utils .run_briefly (self .loop )
971+ self .assertTrue (status ['started' ])
972+ self .assertTrue (status ['stopped' ])
973+ self .assertFalse (status ['finalized' ])
974+ support .gc_collect ()
975+ test_utils .run_briefly (self .loop )
976+ self .assertTrue (status ['finalized' ])
977+
978+ def test_asyncgen_finalization_by_gc_in_other_thread (self ):
979+ # Python issue 34769: If garbage collector runs in another
980+ # thread, async generators will not finalize in debug
981+ # mode.
982+ self .loop ._process_events = mock .Mock ()
983+ self .loop ._write_to_self = mock .Mock ()
984+ self .loop .set_debug (True )
985+ with support .disable_gc ():
986+ status = self .loop .run_until_complete (self .leave_unfinalized_asyncgen ())
987+ while not status ['stopped' ]:
988+ test_utils .run_briefly (self .loop )
989+ self .assertTrue (status ['started' ])
990+ self .assertTrue (status ['stopped' ])
991+ self .assertFalse (status ['finalized' ])
992+ self .loop .run_until_complete (
993+ self .loop .run_in_executor (None , support .gc_collect ))
994+ test_utils .run_briefly (self .loop )
995+ self .assertTrue (status ['finalized' ])
996+
929997
930998class MyProto (asyncio .Protocol ):
931999 done = None
0 commit comments