classification
Title: asyncio.tasks.wait does not allow to set custom exception when return_when=FIRST_EXCEPTION
Type: enhancement Stage: patch review
Components: asyncio Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: asvetlov, giampaolo.rodola, hniksic, pyneda, yselivanov
Priority: normal Keywords: patch

Created on 2018-05-02 00:33 by pyneda, last changed 2018-05-17 16:36 by yselivanov.

Pull Requests
URL Status Linked Edit
PR 6679 open pyneda, 2018-05-02 00:40
Messages (3)
msg316761 - (view) Author: (pyneda) * Date: 2018-05-16 03:42
A possible use case (that at least I couldn't find how to solve) is the possibility to cancel a bunch of futures/coroutine objects which are being awaited using asyncio.tasks.wait() by one of the running coroutines when a task succeded or a specific condition happens by just raising an specific exception.

At the moment this can be done but it will cancel all the coroutines with any exception that is raised and at some occasions this may not be desired.

A simple example:

async def example(num):
    if x == 5:
        raise Exception('Exception that does not cancel')
    elif x == 15:
        raise CancelException()

tasks = [asyncio.ensure_future(example(x)) for x in range(20)]
done, pending = await asyncio.wait(tasks, return_when=FIRST_EXCEPTION, return_on=CancelException)

This wouldn't cancel everything when a normal exception is raised, instead it will when the exception raised is the one that the user expects to be raised in order to cancel everything that is pending. 

In addition, if the user does not specify the Exception type, it uses default Exception so it would keep working exactly as now.
msg316866 - (view) Author: Hrvoje Nikšić (hniksic) Date: 2018-05-16 20:49
"""
At the moment this can be done but it will cancel all the coroutines with any exception that is raised and at some occasions this may not be desired.
"""

Does wait() really "cancel all the coroutines"? The documentation doesn't mention wait() canceling anything it only returns them in the `pending` set. It is gather() and wait_for() that cancel automatically.

If you want to cancel everything on some exception and not on another, you can easily implement the logic yourself, e.g:

tasks = [asyncio.ensure_future(example(x)) for x in range(20)]
done, pending = await asyncio.wait(tasks, return_when=FIRST_EXCEPTION)
for fut in done:
    try:
        fut.result()
    except CancelException:
        for fut in pending:
            fut.cancel()
msg316961 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-17 16:36
This isn't a priority now, so let's postpone the discussion on this until 3.7 is released.
History
Date User Action Args
2018-05-17 16:36:46yselivanovsetmessages: + msg316961
2018-05-16 20:49:09hniksicsetnosy: + hniksic
messages: + msg316866
2018-05-16 03:42:46pynedasetmessages: + msg316761
2018-05-02 00:40:56pynedasetkeywords: + patch
stage: patch review
pull_requests: + pull_request6374
2018-05-02 00:33:54pynedacreate