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