Question presented to candidate:
"Inside async with asyncio.TaskGroup(), one child task raises. Walk me through exactly what happens next: to the other children, to the code in the async with body, and to the exception you finally see. Then: what if the whole thing is cancelled from outside, or wrapped in a timeout?"
What a strong answer should cover:
- On the first child failure the group cancels every other running child and the parent task running the body, then waits for all of them, including their
finally/ cleanup code, before leaving the block. - The result is one
ExceptionGroup("unhandled errors in a TaskGroup", [...])holding every non-cancellation error (children and body);CancelledErrors of the cancelled children are not included. Handle it withexcept*. - The group cancels the parent with
cancel()and undoes it withuncancel()in__aexit__, so its own cancellation turns into the group error, while a real outside cancellation still comes out as a plainCancelledError(andasyncio.timeoutturns it intoTimeoutError). - A child that swallows
CancelledErrorbreaks the contract: it keeps running cleanup or returns normally; always re-raise. KeyboardInterruptandSystemExitare treated specially (re-raised as-is, not grouped); nested groups stay nested.
Clarifying questions expected:
- Should one failing child really stop the others, or are some children "best effort"?
Code / implementation expected: Optional โ a TaskGroup where one child fails and another prints when it is cancelled, caught with except*.