A task group runs several coroutines together and waits for all of them to finish. Sometimes that is more work than necessary. When a group asks several sources for the same thing, the first answer makes the rest pointless, and the remaining tasks should simply stop. Before Python 3.15, the only way out of a group early was to raise an exception.
For example, three mirrors can be queried at once, with the first response ending the group:
import asyncio
class Done(Exception):
pass
async def fetch(mirror, delay):
await asyncio.sleep(delay)
print(f'{mirror} responded')
raise Done
async def main():
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch('mirror-a', 0.3))
tg.create_task(fetch('mirror-b', 0.1))
tg.create_task(fetch('mirror-c', 0.2))
except* Done:
pass
print('done')
asyncio.run(main())
Output:
mirror-b responded
done
The fastest mirror answers, raises Done, and the group cancels the two slower ones. The behaviour is correct, but it requires three separate mechanisms to handle a non-error situation: a custom exception class, a try block, and an except* clause. The last one is necessary because a task group wraps exceptions raised by its tasks in an ExceptionGroup, which a plain except Done would not catch.
Since Python 3.15, a task group can be stopped directly with its cancel method.
import asyncio
async def fetch(mirror, delay, tg):
await asyncio.sleep(delay)
print(f'{mirror} responded')
tg.cancel()
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch('mirror-a', 0.3, tg))
tg.create_task(fetch('mirror-b', 0.1, tg))
tg.create_task(fetch('mirror-c', 0.2, tg))
print('done')
asyncio.run(main())
Output:
mirror-b responded
done
The result is the same and the exception class, the try and the except* are gone. The task calls cancel on the group it belongs to, every unfinished task is cancelled, and the async with block exits normally. Nothing is raised, so the code after the block runs as usual, and except* goes back to being needed only for real failures.
Leave a Comment
Cancel reply