[3.13] gh-124958: fix asyncio.TaskGroup and _PyFuture refcycles (GH-124959) (#125463)

gh-124958: fix asyncio.TaskGroup and _PyFuture refcycles (GH-124959)
(cherry picked from commit d5dbbf4372)

Co-authored-by: Thomas Grainger <tagrain@gmail.com>
This commit is contained in:
Miss Islington (bot) 2024-10-14 17:45:49 +02:00 committed by GitHub
parent 6c81928ba7
commit 4e08c98292
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 147 additions and 15 deletions

View file

@ -66,6 +66,20 @@ class TaskGroup:
return self
async def __aexit__(self, et, exc, tb):
tb = None
try:
return await self._aexit(et, exc)
finally:
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them. It would be nicer to use a try/finally
# in __aexit__ directly but that introduced some diff noise
self._parent_task = None
self._errors = None
self._base_error = None
exc = None
async def _aexit(self, et, exc):
self._exiting = True
if (exc is not None and
@ -122,7 +136,10 @@ class TaskGroup:
assert not self._tasks
if self._base_error is not None:
raise self._base_error
try:
raise self._base_error
finally:
exc = None
if self._parent_cancel_requested:
# If this flag is set we *must* call uncancel().
@ -133,8 +150,14 @@ class TaskGroup:
# Propagate CancelledError if there is one, except if there
# are other errors -- those have priority.
if propagate_cancellation_error is not None and not self._errors:
raise propagate_cancellation_error
try:
if propagate_cancellation_error is not None and not self._errors:
try:
raise propagate_cancellation_error
finally:
exc = None
finally:
propagate_cancellation_error = None
if et is not None and not issubclass(et, exceptions.CancelledError):
self._errors.append(exc)
@ -146,14 +169,14 @@ class TaskGroup:
if self._parent_task.cancelling():
self._parent_task.uncancel()
self._parent_task.cancel()
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them.
try:
me = BaseExceptionGroup('unhandled errors in a TaskGroup', self._errors)
raise me from None
raise BaseExceptionGroup(
'unhandled errors in a TaskGroup',
self._errors,
) from None
finally:
self._errors = None
exc = None
def create_task(self, coro, *, name=None, context=None):
"""Create a new task in this group and return it.