[Python] Cleanup after waiting for first asyncio task to complete: Is it safe to swallow...

Discussão em 'Python' iniciado por Stack, Junho 3, 2025.

  1. Stack

    Stack Membro Participativo

    I have an asyncio function that spawns multiple tasks, any of which can return a result, in which case we want to shut down all the others.

    Right now, it does so in a matter something like the following:

    all_tasks = [asyncio.create_task(ds.get() for ds in data_sources]
    try:
    done, pending = await asyncio.await(
    all_tasks,
    return_when=asyncio.FIRST_COMPLETED
    )
    for task in done:
    if (ex := task.exception()):
    raise ex
    response = await task
    # more handling here, but let's pretend it's just...
    return response
    finally:
    for task in pending:
    try:
    task.cancel()
    await task
    except asyncio.CancelledError:
    continue
    ex = task.exception()
    if ex is not None:
    logger.warn("task failed", ex)
    for task in done:
    ex = task.exception()
    if ex is not None:
    logger.warn("task failed", ex)


    My concern here is that swallowing CancelledError is explicitly documented as bad practice in user code, with safe usage requiring invocation of uncancel().

    Is this in fact unsafe? If so, what would need to be changed to have equivalent functionality without risking corrupting asyncio's internal state?


    (I've viewed a related question which suggests using an asyncio.TaskGroup; however, a TaskGroup waits for all tasks to complete and -- as I read its documentation -- cancels the parent task if any child was cancelled, whereas I want to wait only for the first task to complete, and cancel other children without propagating that cancellation to the parent).

    Continue reading...

Compartilhe esta Página