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