Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asyncio - re-raise exception from a task

I am using asyncio to do some TCP comms. I have a Receive() function that does read() in an infinite loop. This runs as a background task using asyncio.create_task(Receive()).

Now, if the connection is closed by the peer an exception (or could be any other exception) is raised which I catch in the Receive() function. However, I would like to re-raise that exception so the outer code can decide what to do (e.g. re-connect).

Since the exception is raised in a task, I do not know how to retrieve it.

I've tried to create an example of what I mean:

import asyncio

async def divide(x):
    try:
        return 1/x
    except Exception as e:
        print("Divide inner exception: ", e)
        raise   # Re-raise so main() can handle it

async def someFn():
    asyncio.create_task(divide(0))  # Exception is never retrieved
    # await divide(0) # This will raise two exceptions - the original in divide() and in main()

async def main():
    try:
        await someFn()
        # Do other things while someFn() runs
    except Exception as e:
        print("main exception: ", e)

asyncio.run(main())

How do I get the task exception in main()?

like image 865
SimpleOne Avatar asked Aug 12 '26 13:08

SimpleOne


1 Answers

How do I get the task exception in main()?

You can make use of the fact that a task that raises an exception completes, and instead of letting the task run in the background, actually await its completion. This requires the code that creates tasks not to retain the task object returned by create_task() and either return it to the caller or store it to a shared set of tasks. (Libraries such as trio don't even allow blindly spawning a background task and instead require every task to be accompanied by a nursery that defines who handles its exceptions).

For example:

async def someFn():
    # set up a background task, but also return it to the caller
    t = asyncio.create_task(divide(0))
    return t

async def other_things():
    await asyncio.sleep(1)

async def main():
    try:
        task = await someFn()
        # await both the background task and other things, immediately
        # propagating an exception in either
        await asyncio.gather(task, other_things())
    except Exception as e:
        print("main exception: ", e)
like image 108
user4815162342 Avatar answered Aug 14 '26 03:08

user4815162342



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!