|
1 | 1 | # Adapted with permission from the EdgeDB project;
|
2 | 2 | # license: PSFL.
|
3 | 3 |
|
4 |
| - |
| 4 | +import gc |
5 | 5 | import asyncio
|
6 | 6 | import contextvars
|
7 | 7 | import contextlib
|
|
11 | 11 |
|
12 | 12 | from test.test_asyncio.utils import await_without_task
|
13 | 13 |
|
14 |
| - |
15 | 14 | # To prevent a warning "test altered the execution environment"
|
16 | 15 | def tearDownModule():
|
17 | 16 | asyncio.set_event_loop_policy(None)
|
@@ -899,6 +898,95 @@ async def outer():
|
899 | 898 |
|
900 | 899 | await outer()
|
901 | 900 |
|
| 901 | + async def test_exception_refcycles_direct(self): |
| 902 | + """Test that TaskGroup doesn't keep a reference to the raised ExceptionGroup""" |
| 903 | + tg = asyncio.TaskGroup() |
| 904 | + exc = None |
| 905 | + |
| 906 | + class _Done(Exception): |
| 907 | + pass |
| 908 | + |
| 909 | + try: |
| 910 | + async with tg: |
| 911 | + raise _Done |
| 912 | + except ExceptionGroup as e: |
| 913 | + exc = e |
| 914 | + |
| 915 | + self.assertIsNotNone(exc) |
| 916 | + self.assertListEqual(gc.get_referrers(exc), []) |
| 917 | + |
| 918 | + |
| 919 | + async def test_exception_refcycles_errors(self): |
| 920 | + """Test that TaskGroup deletes self._errors, and __aexit__ args""" |
| 921 | + tg = asyncio.TaskGroup() |
| 922 | + exc = None |
| 923 | + |
| 924 | + class _Done(Exception): |
| 925 | + pass |
| 926 | + |
| 927 | + try: |
| 928 | + async with tg: |
| 929 | + raise _Done |
| 930 | + except* _Done as excs: |
| 931 | + exc = excs.exceptions[0] |
| 932 | + |
| 933 | + self.assertIsInstance(exc, _Done) |
| 934 | + self.assertListEqual(gc.get_referrers(exc), []) |
| 935 | + |
| 936 | + |
| 937 | + async def test_exception_refcycles_parent_task(self): |
| 938 | + """Test that TaskGroup deletes self._parent_task""" |
| 939 | + tg = asyncio.TaskGroup() |
| 940 | + exc = None |
| 941 | + |
| 942 | + class _Done(Exception): |
| 943 | + pass |
| 944 | + |
| 945 | + async def coro_fn(): |
| 946 | + async with tg: |
| 947 | + raise _Done |
| 948 | + |
| 949 | + try: |
| 950 | + async with asyncio.TaskGroup() as tg2: |
| 951 | + tg2.create_task(coro_fn()) |
| 952 | + except* _Done as excs: |
| 953 | + exc = excs.exceptions[0].exceptions[0] |
| 954 | + |
| 955 | + self.assertIsInstance(exc, _Done) |
| 956 | + self.assertListEqual(gc.get_referrers(exc), []) |
| 957 | + |
| 958 | + async def test_exception_refcycles_propagate_cancellation_error(self): |
| 959 | + """Test that TaskGroup deletes propagate_cancellation_error""" |
| 960 | + tg = asyncio.TaskGroup() |
| 961 | + exc = None |
| 962 | + |
| 963 | + try: |
| 964 | + async with asyncio.timeout(-1): |
| 965 | + async with tg: |
| 966 | + await asyncio.sleep(0) |
| 967 | + except TimeoutError as e: |
| 968 | + exc = e.__cause__ |
| 969 | + |
| 970 | + self.assertIsInstance(exc, asyncio.CancelledError) |
| 971 | + self.assertListEqual(gc.get_referrers(exc), []) |
| 972 | + |
| 973 | + async def test_exception_refcycles_base_error(self): |
| 974 | + """Test that TaskGroup deletes self._base_error""" |
| 975 | + class MyKeyboardInterrupt(KeyboardInterrupt): |
| 976 | + pass |
| 977 | + |
| 978 | + tg = asyncio.TaskGroup() |
| 979 | + exc = None |
| 980 | + |
| 981 | + try: |
| 982 | + async with tg: |
| 983 | + raise MyKeyboardInterrupt |
| 984 | + except MyKeyboardInterrupt as e: |
| 985 | + exc = e |
| 986 | + |
| 987 | + self.assertIsNotNone(exc) |
| 988 | + self.assertListEqual(gc.get_referrers(exc), []) |
| 989 | + |
902 | 990 |
|
903 | 991 | if __name__ == "__main__":
|
904 | 992 | unittest.main()
|
0 commit comments