mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	[3.13] gh-128588: gh-128550: remove eager tasks optimization that missed and introduced incorrect cancellations (GH-129063) (#129089)
gh-128588: gh-128550: remove eager tasks optimization that missed and introduced incorrect cancellations (GH-129063)
(cherry picked from commit ed6934e71e)
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
			
			
This commit is contained in:
		
							parent
							
								
									22e9faf08a
								
							
						
					
					
						commit
						a1c48a750c
					
				
					 3 changed files with 56 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -1032,6 +1032,56 @@ class MyKeyboardInterrupt(KeyboardInterrupt):
 | 
			
		|||
        self.assertListEqual(gc.get_referrers(exc), [])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    async def test_cancels_task_if_created_during_creation(self):
 | 
			
		||||
        # regression test for gh-128550
 | 
			
		||||
        ran = False
 | 
			
		||||
        class MyError(Exception):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        exc = None
 | 
			
		||||
        try:
 | 
			
		||||
            async with asyncio.TaskGroup() as tg:
 | 
			
		||||
                async def third_task():
 | 
			
		||||
                    raise MyError("third task failed")
 | 
			
		||||
 | 
			
		||||
                async def second_task():
 | 
			
		||||
                    nonlocal ran
 | 
			
		||||
                    tg.create_task(third_task())
 | 
			
		||||
                    with self.assertRaises(asyncio.CancelledError):
 | 
			
		||||
                        await asyncio.sleep(0)  # eager tasks cancel here
 | 
			
		||||
                        await asyncio.sleep(0)  # lazy tasks cancel here
 | 
			
		||||
                    ran = True
 | 
			
		||||
 | 
			
		||||
                tg.create_task(second_task())
 | 
			
		||||
        except* MyError as excs:
 | 
			
		||||
            exc = excs.exceptions[0]
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(ran)
 | 
			
		||||
        self.assertIsInstance(exc, MyError)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    async def test_cancellation_does_not_leak_out_of_tg(self):
 | 
			
		||||
        class MyError(Exception):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        async def throw_error():
 | 
			
		||||
            raise MyError
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            async with asyncio.TaskGroup() as tg:
 | 
			
		||||
                tg.create_task(throw_error())
 | 
			
		||||
        except* MyError:
 | 
			
		||||
            pass
 | 
			
		||||
        else:
 | 
			
		||||
            self.fail("should have raised one MyError in group")
 | 
			
		||||
 | 
			
		||||
        # if this test fails this current task will be cancelled
 | 
			
		||||
        # outside the task group and inside unittest internals
 | 
			
		||||
        # we yield to the event loop with sleep(0) so that
 | 
			
		||||
        # cancellation happens here and error is more understandable
 | 
			
		||||
        await asyncio.sleep(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase):
 | 
			
		||||
    loop_factory = asyncio.EventLoop
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue