@@ -237,33 +237,6 @@ async def inner():
237237 with self .assertRaises (FooException ):
238238 await foo ()
239239
240- async def test_wait_for_self_cancellation (self ):
241- async def inner ():
242- try :
243- await asyncio .sleep (0.3 )
244- except asyncio .CancelledError :
245- try :
246- await asyncio .sleep (0.3 )
247- except asyncio .CancelledError :
248- await asyncio .sleep (0.3 )
249-
250- return 42
251-
252- inner_task = asyncio .create_task (inner ())
253-
254- wait = asyncio .wait_for (inner_task , timeout = 0.1 )
255-
256- # Test that wait_for itself is properly cancellable
257- # even when the initial task holds up the initial cancellation.
258- task = asyncio .create_task (wait )
259- await asyncio .sleep (0.2 )
260- task .cancel ()
261-
262- with self .assertRaises (asyncio .CancelledError ):
263- await task
264-
265- self .assertEqual (await inner_task , 42 )
266-
267240 async def _test_cancel_wait_for (self , timeout ):
268241 loop = asyncio .get_running_loop ()
269242
@@ -289,6 +262,106 @@ async def test_cancel_blocking_wait_for(self):
289262 async def test_cancel_wait_for (self ):
290263 await self ._test_cancel_wait_for (60.0 )
291264
265+ async def test_wait_for_cancel_suppressed (self ):
266+ # GH-86296: Supressing CancelledError is discouraged
267+ # but if a task subpresses CancelledError and returns a value,
268+ # `wait_for` should return the value instead of raising CancelledError.
269+ # This is the same behavior as `asyncio.timeout`.
270+
271+ async def return_42 ():
272+ try :
273+ await asyncio .sleep (10 )
274+ except asyncio .CancelledError :
275+ return 42
276+
277+ res = await asyncio .wait_for (return_42 (), timeout = 0.1 )
278+ self .assertEqual (res , 42 )
279+
280+
281+ async def test_wait_for_issue86296 (self ):
282+ # GH-86296: The task should get cancelled and not run to completion.
283+ # inner completes in one cycle of the event loop so it
284+ # completes before the task is cancelled.
285+
286+ async def inner ():
287+ return 'done'
288+
289+ inner_task = asyncio .create_task (inner ())
290+ reached_end = False
291+
292+ async def wait_for_coro ():
293+ await asyncio .wait_for (inner_task , timeout = 100 )
294+ await asyncio .sleep (1 )
295+ nonlocal reached_end
296+ reached_end = True
297+
298+ task = asyncio .create_task (wait_for_coro ())
299+ self .assertFalse (task .done ())
300+ # Run the task
301+ await asyncio .sleep (0 )
302+ task .cancel ()
303+ with self .assertRaises (asyncio .CancelledError ):
304+ await task
305+ self .assertTrue (inner_task .done ())
306+ self .assertEqual (await inner_task , 'done' )
307+ self .assertFalse (reached_end )
308+
309+
310+ class WaitForShieldTests (unittest .IsolatedAsyncioTestCase ):
311+
312+ async def test_zero_timeout (self ):
313+ # `asyncio.shield` creates a new task which wraps the passed in
314+ # awaitable and shields it from cancellation so with timeout=0
315+ # the task returned by `asyncio.shield` aka shielded_task gets
316+ # cancelled immediately and the task wrapped by it is scheduled
317+ # to run.
318+
319+ async def coro ():
320+ await asyncio .sleep (0.01 )
321+ return 'done'
322+
323+ task = asyncio .create_task (coro ())
324+ with self .assertRaises (asyncio .TimeoutError ):
325+ shielded_task = asyncio .shield (task )
326+ await asyncio .wait_for (shielded_task , timeout = 0 )
327+
328+ # Task is running in background
329+ self .assertFalse (task .done ())
330+ self .assertFalse (task .cancelled ())
331+ self .assertTrue (shielded_task .cancelled ())
332+
333+ # Wait for the task to complete
334+ await asyncio .sleep (0.1 )
335+ self .assertTrue (task .done ())
336+
337+
338+ async def test_none_timeout (self ):
339+ # With timeout=None the timeout is disabled so it
340+ # runs till completion.
341+ async def coro ():
342+ await asyncio .sleep (0.1 )
343+ return 'done'
344+
345+ task = asyncio .create_task (coro ())
346+ await asyncio .wait_for (asyncio .shield (task ), timeout = None )
347+
348+ self .assertTrue (task .done ())
349+ self .assertEqual (await task , "done" )
350+
351+ async def test_shielded_timeout (self ):
352+ # shield prevents the task from being cancelled.
353+ async def coro ():
354+ await asyncio .sleep (0.1 )
355+ return 'done'
356+
357+ task = asyncio .create_task (coro ())
358+ with self .assertRaises (asyncio .TimeoutError ):
359+ await asyncio .wait_for (asyncio .shield (task ), timeout = 0.01 )
360+
361+ self .assertFalse (task .done ())
362+ self .assertFalse (task .cancelled ())
363+ self .assertEqual (await task , "done" )
364+
292365
293366if __name__ == '__main__' :
294367 unittest .main ()
0 commit comments