From 0e4e8d4949c1ed8c66f1e436afcb642688bc6305 Mon Sep 17 00:00:00 2001 From: "Joe S. Boyle" Date: Thu, 9 Mar 2023 19:11:04 +0000 Subject: [PATCH 1/6] Add a docstring to the public methods of asyncio.TaskGroup --- Lib/asyncio/taskgroups.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 911419e1769c17..19959d9194fca1 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -10,7 +10,30 @@ class TaskGroup: + """An asynchronous context manager for managing groups of tasks. + Example use: + + async with asyncio.TaskGroup() as group: + task1 = group.create_task(some_coroutine(...)) + task2 = group.create_task(other_coroutine(...)) + + print("Both tasks have completed now.") + + All tasks are awaited when the context manager exits. + + Once the context has been exited it's guaranteed that no tasks + from the group remain alive. That is, all tasks are guaranteed + to have either finished successfully or have been cancelled. + + Any exceptions other than asyncio.CancelledError's raised within + tasks cause each of the remaining tasks to be cancelled and the + context to be exited. + + Once all tasks have finished, if any tasks have failed with an + exception other than asyncio.CancelledError, those exceptions are + combined in an ExceptionGroup or BaseExceptionGroup and re-raised. + """ def __init__(self): self._entered = False self._exiting = False @@ -135,6 +158,10 @@ async def __aexit__(self, et, exc, tb): self._errors = None def create_task(self, coro, *, name=None, context=None): + """Schedule a coroutine and return a task representing it. + + Raises a RuntimeError if the task cannot be created. + """ if not self._entered: raise RuntimeError(f"TaskGroup {self!r} has not been entered") if self._exiting and not self._tasks: From 32e259fb77b191e2f10aeab8b1364e50fe525a70 Mon Sep 17 00:00:00 2001 From: "Joe S. Boyle" Date: Sun, 12 Mar 2023 12:40:58 +0000 Subject: [PATCH 2/6] Update TaskGroup::create_task's docstring --- Lib/asyncio/taskgroups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 19959d9194fca1..22f9dc496812a9 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -158,9 +158,9 @@ async def __aexit__(self, et, exc, tb): self._errors = None def create_task(self, coro, *, name=None, context=None): - """Schedule a coroutine and return a task representing it. + """Create a new task in this group and return it. - Raises a RuntimeError if the task cannot be created. + Matches the call signature of asyncio.create_task. """ if not self._entered: raise RuntimeError(f"TaskGroup {self!r} has not been entered") From a5645c8f7727f7ef40627026be1822b391f05791 Mon Sep 17 00:00:00 2001 From: "Joe S. Boyle" Date: Mon, 13 Mar 2023 21:42:06 +0000 Subject: [PATCH 3/6] Trim the docstrings of asyncio.TaskGroup and fix a whitespace issue --- Lib/asyncio/taskgroups.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 22f9dc496812a9..a81eca9c20dd0f 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -17,7 +17,7 @@ class TaskGroup: async with asyncio.TaskGroup() as group: task1 = group.create_task(some_coroutine(...)) task2 = group.create_task(other_coroutine(...)) - + print("Both tasks have completed now.") All tasks are awaited when the context manager exits. @@ -27,12 +27,8 @@ class TaskGroup: to have either finished successfully or have been cancelled. Any exceptions other than asyncio.CancelledError's raised within - tasks cause each of the remaining tasks to be cancelled and the - context to be exited. - - Once all tasks have finished, if any tasks have failed with an - exception other than asyncio.CancelledError, those exceptions are - combined in an ExceptionGroup or BaseExceptionGroup and re-raised. + tasks will cancel all remaining tasks and exit the context. These + exceptions will be combined into an ExceptionGroup and re-raised. """ def __init__(self): self._entered = False @@ -159,8 +155,7 @@ async def __aexit__(self, et, exc, tb): def create_task(self, coro, *, name=None, context=None): """Create a new task in this group and return it. - - Matches the call signature of asyncio.create_task. + Similar to `asyncio.create_task`. """ if not self._entered: raise RuntimeError(f"TaskGroup {self!r} has not been entered") From d991d95109735a363db0f6dc32c0e43cce702684 Mon Sep 17 00:00:00 2001 From: "Joe S. Boyle" Date: Mon, 13 Mar 2023 21:44:43 +0000 Subject: [PATCH 4/6] Trim TaskGroup docstring even more --- Lib/asyncio/taskgroups.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index a81eca9c20dd0f..5c9dbdf72ec98c 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -22,10 +22,6 @@ class TaskGroup: All tasks are awaited when the context manager exits. - Once the context has been exited it's guaranteed that no tasks - from the group remain alive. That is, all tasks are guaranteed - to have either finished successfully or have been cancelled. - Any exceptions other than asyncio.CancelledError's raised within tasks will cancel all remaining tasks and exit the context. These exceptions will be combined into an ExceptionGroup and re-raised. From f710502e81a57e75812ddc5d79d9bbe2f1205c0b Mon Sep 17 00:00:00 2001 From: "Joe S. Boyle" Date: Tue, 14 Mar 2023 21:29:50 +0000 Subject: [PATCH 5/6] Remove incorrect apostrophe --- Lib/asyncio/taskgroups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 5c9dbdf72ec98c..530c432d6eb27a 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -22,7 +22,7 @@ class TaskGroup: All tasks are awaited when the context manager exits. - Any exceptions other than asyncio.CancelledError's raised within + Any exceptions other than asyncio.CancelledErrors raised within tasks will cancel all remaining tasks and exit the context. These exceptions will be combined into an ExceptionGroup and re-raised. """ From 8a29e2123b01041953a1110002cfc81c6bd904d0 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 14 Mar 2023 16:45:31 -0700 Subject: [PATCH 6/6] Apply suggestions from code review --- Lib/asyncio/taskgroups.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 530c432d6eb27a..0fdea3697ece3d 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -10,21 +10,20 @@ class TaskGroup: - """An asynchronous context manager for managing groups of tasks. + """Asynchronous context manager for managing groups of tasks. Example use: async with asyncio.TaskGroup() as group: task1 = group.create_task(some_coroutine(...)) task2 = group.create_task(other_coroutine(...)) - print("Both tasks have completed now.") All tasks are awaited when the context manager exits. - Any exceptions other than asyncio.CancelledErrors raised within - tasks will cancel all remaining tasks and exit the context. These - exceptions will be combined into an ExceptionGroup and re-raised. + Any exceptions other than `asyncio.CancelledError` raised within + a task will cancel all remaining tasks and wait for them to exit. + The exceptions are then combined and raised as an `ExceptionGroup`. """ def __init__(self): self._entered = False @@ -151,6 +150,7 @@ async def __aexit__(self, et, exc, tb): def create_task(self, coro, *, name=None, context=None): """Create a new task in this group and return it. + Similar to `asyncio.create_task`. """ if not self._entered: