Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python asyncio: handling exceptions in gather() - documentation unclear?

Tags:

The documentation for asyncio.gather says that

If return_exceptions is False (default), the first raised exception is immediately propagated to the task that awaits on gather(). Other awaitables in the aws sequence won’t be cancelled and will continue to run.

However, from a simple test it seems that if one of the tasks raises an exception when return_exceptions is False, all other awaitable are cancelled (or to be more precise, in case the terminology is not clear to me, the other awaitables do not finish their job):

import asyncio  async def factorial(name, number, raise_exception=False):     # If raise_exception is True, will raise an exception when     # the loop counter > 3     f = 1     for i in range(2, number + 1):         print(f'  Task {name}: Compute factorial({i})...')          if raise_exception and i > 3:             print(f'  Task {name}: raising Exception')             raise Exception(f'Bad Task {name}')          await asyncio.sleep(1)         f *= i     print(f'==>> Task {name} DONE: factorial({number}) = {f}')     return f  async def main():     tasks = [factorial('A', 5),  # this will not be finished              factorial('B', 10, raise_exception=True),              factorial('C', 2)]      try:         results = await asyncio.gather(*tasks)         print('Results:', results)     except Exception as e:         print('Got an exception:', e)  asyncio.run(main()) 

What this piece of code is doing, just to make it simpler, it defines 3 tasks and call asyncio.gather() on them. One of the tasks raises an exception before one of the others is done, and this other task is not finished.

Actually, I cannot even make sense with what the documentations says - if an exception is raised and caught by the task awaiting on gather, I would not even be able to get the returned results (even if the other task would, somehow, get done).

Am I missing anything, or is there a problem with the documentation?

This was tested with Python 3.7.2.

like image 574
amitr Avatar asked Mar 04 '19 16:03

amitr


People also ask

Does Asyncio gather block?

Specifically, code that runs in the asyncio event loop must not block - all blocking calls must be replaced with non-blocking versions that yield control to the event loop.

What is Asyncio gather Python?

2 weeks ago. by Kalsoom Bibi. Asynchronous input/output or asyncio is a programming methodology that uses an event loop or a single thread to accomplish high concurrency. Python is not the only language or framework that uses the approach; JavaScript's NodeJS is maybe the most well-known.

How do I recover Task exception?

Retrieving exceptions for each taskexception() : Returns the exception object raised by the coroutine, None if no exception as raised. result() : Returns the result of the coroutine, re-throws the exception if the coroutine raised an exception.


1 Answers

I've run your code and got the following output, as expected from documentation.

  Task C: Compute factorial(2)...   Task A: Compute factorial(2)...   Task B: Compute factorial(2)... ==>> Task C DONE: factorial(2) = 2   Task A: Compute factorial(3)...   Task B: Compute factorial(3)...   Task A: Compute factorial(4)...   Task B: Compute factorial(4)...   Task B: raising Exception Got an exception: Bad Task B   Task A: Compute factorial(5)... ==>> Task A DONE: factorial(5) = 120 

What's going on

  1. Tasks A,B and C are submitted to the queue;
  2. All tasks are running while C finishes earliest.
  3. Task B raises and exception.
  4. The await asyncio.gater() returns immediately and print('Got an exception:', e) to the screen.
  5. Task A continues to run and print "==>> Task A DONE ..."

What's wrong with your test

As @deceze commented, your program exited immediately after the exception was caught and main() returns. Thus, the tasks A and C are terminated because the entire process dies, not because of cancellation.

To fix it, add await asyncio.sleep(20) to the end of the main() function.

like image 186
gdlmx Avatar answered Sep 19 '22 05:09

gdlmx