Question presented to candidate:
"You need to call three services concurrently from an async handler. Most older code would use asyncio.gather for that. Python 3.11 added asyncio.TaskGroup. What is it, and why do people say it should replace gather?"
What a strong answer should cover:
- What it is: an async context manager (
async with asyncio.TaskGroup() as tg:) whosetg.create_task()starts child tasks; leaving the block waits for every child β the idea called structured concurrency (added in Python 3.11). - The failure behaviour: when one child raises, TaskGroup cancels the other children and the body, waits for them, then raises an
ExceptionGroupwith every error; plaingatherraises the first error and leaves the siblings running unattended. - Errors are not lost: gather silently drops the 2nd, 3rd... errors (unless
return_exceptions=True, which turns errors into values you must check); TaskGroup keeps them all, handled withexcept*. - Results: gather returns a list in argument order; with TaskGroup you keep the
Taskobjects and call.result()after the block. - Honest trade-off: TaskGroup is not a drop-in replacement β gather with
return_exceptions=Trueis still the right tool when you want partial results; TaskGroup needs 3.11+.
Clarifying questions expected:
- If one of the calls fails, do you want the others cancelled, or do you want whatever results succeeded?
- Which Python version does the service run on (TaskGroup needs 3.11+)?
Code / implementation expected: Optional β the same three calls written with gather and with TaskGroup, and what happens when one fails.