@@ -133,6 +133,78 @@ async def blah():
133
133
asyncio .get_event_loop ().run_until_complete (info ["coro" ])
134
134
135
135
136
+ it "can shutdown async gens" :
137
+ info1 = []
138
+ info2 = []
139
+ info3 = []
140
+
141
+ original = asyncio .get_event_loop ()
142
+
143
+ async def my_generator (info ):
144
+ try :
145
+ info .append (1 )
146
+ yield
147
+ info .append (2 )
148
+ yield
149
+ info .append (3 )
150
+ except asyncio .CancelledError :
151
+ info .append ("cancelled" )
152
+ raise
153
+ finally :
154
+ info .append (("done" , __import__ ("sys" ).exc_info ()[0 ]))
155
+
156
+ # Test that the outside loop isn't affected by the inside loop
157
+ outside_gen = my_generator (info1 )
158
+
159
+ async def outside1 ():
160
+ await outside_gen .__anext__ ()
161
+ await outside_gen .__anext__ ()
162
+
163
+ original .run_until_complete (outside1 ())
164
+ assert info1 == [1 , 2 ]
165
+
166
+ # The way python asyncio works
167
+ # Means that by defining this outside our OverrideLoop
168
+ # The weakref held against it in the _asyncgens set on the loop
169
+ # Will remain so that our shutdown_asyncgens function may work
170
+ ag = my_generator (info2 )
171
+
172
+ with OverrideLoop (new_loop = True ) as custom_loop :
173
+ assert info2 == []
174
+ assert info3 == []
175
+
176
+ async def doit ():
177
+ ag2 = my_generator (info3 )
178
+ assert set (asyncio .get_event_loop ()._asyncgens ) == set ()
179
+ await ag2 .__anext__ ()
180
+ assert set (asyncio .get_event_loop ()._asyncgens ) == set ([ag2 ])
181
+ await ag .__anext__ ()
182
+ assert set (asyncio .get_event_loop ()._asyncgens ) == set ([ag2 , ag ])
183
+ await ag .__anext__ ()
184
+ assert info3 == [1 ]
185
+
186
+ custom_loop .run_until_complete (doit ())
187
+ assert list (custom_loop .loop ._asyncgens ) == [ag ]
188
+ assert info3 == [1 ]
189
+ assert info2 == [1 , 2 ]
190
+
191
+ assert asyncio .get_event_loop () is original
192
+ assert not original .is_closed ()
193
+
194
+ assert info3 == [1 , "cancelled" , ("done" , asyncio .CancelledError )]
195
+ assert info2 == [1 , 2 , "cancelled" , ("done" , asyncio .CancelledError )]
196
+ assert info1 == [1 , 2 ]
197
+
198
+ async def outside2 ():
199
+ try :
200
+ await outside_gen .__anext__ ()
201
+ except StopAsyncIteration :
202
+ pass
203
+
204
+ # Test that the outside loop isn't affected by the inside loop
205
+ original .run_until_complete (outside2 ())
206
+ assert info1 == [1 , 2 , 3 , ("done" , None )]
207
+
136
208
describe "testing autouse" :
137
209
138
210
@pytest .fixture (autouse = True )
0 commit comments