@@ -915,6 +915,74 @@ def test_run_forever_pre_stopped(self):
915
915
self .loop .run_forever ()
916
916
self .loop ._selector .select .assert_called_once_with (0 )
917
917
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
+
918
986
919
987
class MyProto (asyncio .Protocol ):
920
988
done = None
0 commit comments