@@ -907,6 +907,74 @@ def test_run_forever_pre_stopped(self):
907
907
self .loop .run_forever ()
908
908
self .loop ._selector .select .assert_called_once_with (0 )
909
909
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
+
910
978
911
979
class MyProto (asyncio .Protocol ):
912
980
done = None
0 commit comments