mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-37658: Fix asyncio.wait_for() to respect waited task status (#21894)
Currently, if `asyncio.wait_for()` itself is cancelled it will always raise `CancelledError` regardless if the underlying task is still running. This is similar to a race with the timeout, which is handled already.
This commit is contained in:
		
							parent
							
								
									c517fc7121
								
							
						
					
					
						commit
						a2118a1462
					
				
					 3 changed files with 24 additions and 3 deletions
				
			
		|  | @ -465,9 +465,12 @@ async def wait_for(fut, timeout, *, loop=None): | ||||||
|         try: |         try: | ||||||
|             await waiter |             await waiter | ||||||
|         except exceptions.CancelledError: |         except exceptions.CancelledError: | ||||||
|             fut.remove_done_callback(cb) |             if fut.done(): | ||||||
|             fut.cancel() |                 return fut.result() | ||||||
|             raise |             else: | ||||||
|  |                 fut.remove_done_callback(cb) | ||||||
|  |                 fut.cancel() | ||||||
|  |                 raise | ||||||
| 
 | 
 | ||||||
|         if fut.done(): |         if fut.done(): | ||||||
|             return fut.result() |             return fut.result() | ||||||
|  |  | ||||||
|  | @ -1120,6 +1120,22 @@ def gen(): | ||||||
|         res = loop.run_until_complete(task) |         res = loop.run_until_complete(task) | ||||||
|         self.assertEqual(res, "ok") |         self.assertEqual(res, "ok") | ||||||
| 
 | 
 | ||||||
|  |     def test_wait_for_cancellation_race_condition(self): | ||||||
|  |         def gen(): | ||||||
|  |             yield 0.1 | ||||||
|  |             yield 0.1 | ||||||
|  |             yield 0.1 | ||||||
|  |             yield 0.1 | ||||||
|  | 
 | ||||||
|  |         loop = self.new_test_loop(gen) | ||||||
|  | 
 | ||||||
|  |         fut = self.new_future(loop) | ||||||
|  |         loop.call_later(0.1, fut.set_result, "ok") | ||||||
|  |         task = loop.create_task(asyncio.wait_for(fut, timeout=1)) | ||||||
|  |         loop.call_later(0.1, task.cancel) | ||||||
|  |         res = loop.run_until_complete(task) | ||||||
|  |         self.assertEqual(res, "ok") | ||||||
|  | 
 | ||||||
|     def test_wait_for_waits_for_task_cancellation(self): |     def test_wait_for_waits_for_task_cancellation(self): | ||||||
|         loop = asyncio.new_event_loop() |         loop = asyncio.new_event_loop() | ||||||
|         self.addCleanup(loop.close) |         self.addCleanup(loop.close) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | :meth:`asyncio.wait_for` now properly handles races between cancellation of | ||||||
|  | itself and the completion of the wrapped awaitable. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Elvis Pranskevichus
						Elvis Pranskevichus