asyncio.gather и обработка ошибок
У asyncio.gather есть две заметно разные модели поведения, и выбор между ними легко пропустить.
По умолчанию: первая ошибка «всплывает»
Если любая из корутин бросит исключение, gather немедленно пробросит его наверх. Остальные задачи при этом не отменяются автоматически — они продолжат выполняться в фоне, и про них легко забыть.
import asyncio
async def main():
await asyncio.gather(task_a(), task_b(), task_c())
return_exceptions=True
С этим флагом gather дождётся всех корутин и вернёт список, где на месте упавших задач будут сами объекты исключений. Удобно, когда частичный успех допустим:
results = await asyncio.gather(*tasks, return_exceptions=True)
for r in results:
if isinstance(r, Exception):
log.warning("задача упала: %r", r)
А что в 3.11+
Если нужна семантика «всё или ничего» с гарантированной отменой остальных задач при ошибке — берите asyncio.TaskGroup. Он отменяет соседние задачи и собирает ошибки в ExceptionGroup.