Skip to content

Commit 5bcc3d5

Browse files
committed
pythongh-115957: Close coroutine if the TaskGroup is inactive
1 parent 7af063d commit 5bcc3d5

File tree

4 files changed

+23
-10
lines changed

4 files changed

+23
-10
lines changed

Doc/library/asyncio-task.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ and reliable way to wait for all tasks in the group to finish.
335335
Create a task in this task group.
336336
The signature matches that of :func:`asyncio.create_task`.
337337

338+
.. versionchanged:: 3.13
339+
340+
Close the given coroutine if the task group is not active.
341+
338342
Example::
339343

340344
async def main():

Doc/whatsnew/3.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ Other Language Changes
185185

186186
(Contributed by Sebastian Pipping in :gh:`115623`.)
187187

188+
* When :func:`asyncio.TaskGroup.create_task` is called on an inactive
189+
:class:`asyncio.TaskGroup`, the given coroutine will be closed (which
190+
prevents a :exc:`RuntimeWarning`).
191+
192+
(Contributed by Arthur Tacca and Jason Zhang in :gh:`115957`.)
188193

189194
New Modules
190195
===========

Lib/asyncio/taskgroups.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,13 @@ def create_task(self, coro, *, name=None, context=None):
154154
Similar to `asyncio.create_task`.
155155
"""
156156
if not self._entered:
157+
coro.close()
157158
raise RuntimeError(f"TaskGroup {self!r} has not been entered")
158159
if self._exiting and not self._tasks:
160+
coro.close()
159161
raise RuntimeError(f"TaskGroup {self!r} is finished")
160162
if self._aborting:
163+
coro.close()
161164
raise RuntimeError(f"TaskGroup {self!r} is shutting down")
162165
if context is None:
163166
task = self._loop.create_task(coro, name=name)

Lib/test/test_asyncio/test_taskgroups.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -738,10 +738,7 @@ async def coro2(g):
738738
await asyncio.sleep(1)
739739
except asyncio.CancelledError:
740740
with self.assertRaises(RuntimeError):
741-
g.create_task(c1 := coro1())
742-
# We still have to await c1 to avoid a warning
743-
with self.assertRaises(ZeroDivisionError):
744-
await c1
741+
g.create_task(coro1())
745742

746743
with self.assertRaises(ExceptionGroup) as cm:
747744
async with taskgroups.TaskGroup() as g:
@@ -803,16 +800,12 @@ async def test_taskgroup_finished(self):
803800
coro = asyncio.sleep(0)
804801
with self.assertRaisesRegex(RuntimeError, "is finished"):
805802
tg.create_task(coro)
806-
# We still have to await coro to avoid a warning
807-
await coro
808803

809804
async def test_taskgroup_not_entered(self):
810805
tg = taskgroups.TaskGroup()
811806
coro = asyncio.sleep(0)
812807
with self.assertRaisesRegex(RuntimeError, "has not been entered"):
813808
tg.create_task(coro)
814-
# We still have to await coro to avoid a warning
815-
await coro
816809

817810
async def test_taskgroup_without_parent_task(self):
818811
tg = taskgroups.TaskGroup()
@@ -821,8 +814,16 @@ async def test_taskgroup_without_parent_task(self):
821814
coro = asyncio.sleep(0)
822815
with self.assertRaisesRegex(RuntimeError, "has not been entered"):
823816
tg.create_task(coro)
824-
# We still have to await coro to avoid a warning
825-
await coro
817+
818+
def test_coro_closed_when_tg_closed(self):
819+
async def run_coro_after_tg_closes():
820+
async with taskgroups.TaskGroup() as tg:
821+
pass
822+
coro = asyncio.sleep(0)
823+
with self.assertRaisesRegex(RuntimeError, "is finished"):
824+
tg.create_task(coro)
825+
loop = asyncio.get_event_loop()
826+
loop.run_until_complete(run_coro_after_tg_closes())
826827

827828

828829
if __name__ == "__main__":

0 commit comments

Comments
 (0)