diff options
author | Thomas Grainger <tagrain@gmail.com> | 2024-10-14 15:19:56 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-14 15:19:56 (GMT) |
commit | d5dbbf4372cd3dbf3eead1cc70ddc4261c061fd9 (patch) | |
tree | 81f9098b3e6857cad85f38790a96765968b4074b /Lib/test/test_asyncio/test_taskgroups.py | |
parent | 45df264f3ffbc0893cbfd257131d3abe21043786 (diff) | |
download | cpython-d5dbbf4372cd3dbf3eead1cc70ddc4261c061fd9.zip cpython-d5dbbf4372cd3dbf3eead1cc70ddc4261c061fd9.tar.gz cpython-d5dbbf4372cd3dbf3eead1cc70ddc4261c061fd9.tar.bz2 |
gh-124958: fix asyncio.TaskGroup and _PyFuture refcycles (#124959)
Diffstat (limited to 'Lib/test/test_asyncio/test_taskgroups.py')
-rw-r--r-- | Lib/test/test_asyncio/test_taskgroups.py | 92 |
1 files changed, 90 insertions, 2 deletions
diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index 4852536..138f59e 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -1,7 +1,7 @@ # Adapted with permission from the EdgeDB project; # license: PSFL. - +import gc import asyncio import contextvars import contextlib @@ -11,7 +11,6 @@ import warnings from test.test_asyncio.utils import await_without_task - # To prevent a warning "test altered the execution environment" def tearDownModule(): asyncio.set_event_loop_policy(None) @@ -899,6 +898,95 @@ class TestTaskGroup(unittest.IsolatedAsyncioTestCase): await outer() + async def test_exception_refcycles_direct(self): + """Test that TaskGroup doesn't keep a reference to the raised ExceptionGroup""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + try: + async with tg: + raise _Done + except ExceptionGroup as e: + exc = e + + self.assertIsNotNone(exc) + self.assertListEqual(gc.get_referrers(exc), []) + + + async def test_exception_refcycles_errors(self): + """Test that TaskGroup deletes self._errors, and __aexit__ args""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + try: + async with tg: + raise _Done + except* _Done as excs: + exc = excs.exceptions[0] + + self.assertIsInstance(exc, _Done) + self.assertListEqual(gc.get_referrers(exc), []) + + + async def test_exception_refcycles_parent_task(self): + """Test that TaskGroup deletes self._parent_task""" + tg = asyncio.TaskGroup() + exc = None + + class _Done(Exception): + pass + + async def coro_fn(): + async with tg: + raise _Done + + try: + async with asyncio.TaskGroup() as tg2: + tg2.create_task(coro_fn()) + except* _Done as excs: + exc = excs.exceptions[0].exceptions[0] + + self.assertIsInstance(exc, _Done) + self.assertListEqual(gc.get_referrers(exc), []) + + async def test_exception_refcycles_propagate_cancellation_error(self): + """Test that TaskGroup deletes propagate_cancellation_error""" + tg = asyncio.TaskGroup() + exc = None + + try: + async with asyncio.timeout(-1): + async with tg: + await asyncio.sleep(0) + except TimeoutError as e: + exc = e.__cause__ + + self.assertIsInstance(exc, asyncio.CancelledError) + self.assertListEqual(gc.get_referrers(exc), []) + + async def test_exception_refcycles_base_error(self): + """Test that TaskGroup deletes self._base_error""" + class MyKeyboardInterrupt(KeyboardInterrupt): + pass + + tg = asyncio.TaskGroup() + exc = None + + try: + async with tg: + raise MyKeyboardInterrupt + except MyKeyboardInterrupt as e: + exc = e + + self.assertIsNotNone(exc) + self.assertListEqual(gc.get_referrers(exc), []) + if __name__ == "__main__": unittest.main() |