Structured Concurrency
A track of P33 · Asyncio & Coroutines.
TaskGroups give concurrent work a lifetime it cannot outlive: scoped tasks, sibling cancellation on the first failure, and exception groups - error handling you can reason about locally.
You launch ten downloads with asyncio.gather; one 404s. The exception surfaces,
you handle it — and the other nine keep running in the background, orphaned,
holding sockets, logging into the void. gather started them but nothing owns
their lifetime. That gap is what structured concurrency closes.
asyncio.TaskGroup (Python 3.11+) gives concurrent work a scope it cannot outlive.
Inside async with asyncio.TaskGroup() as tg: you spawn tasks with
tg.create_task(...), and the block simply does not exit until every one of them
has finished — concurrent work now has the same lifetime as the code that started
it, exactly like a function returns only when its body is done. And if one task
raises, the group cancels its siblings, waits for them to unwind, and only
then propagates the error — as an ExceptionGroup, because several tasks can
fail at once and none should be silently dropped (you handle it with except*).
The kernel rung runs a real TaskGroup, including the failure path where a raising
task takes its siblings down with it. The payoff is that error handling and
cancellation stop being a scatter of futures you babysit and become local and
predictable — you reason about one block at a time.