Reputation: 1514
I have an asyncio/Python program with two asyncio tasks:
I want my entire program to exit after the first crash. I cannot get it to happen.
import asyncio
import time
def infinite_while():
while True:
time.sleep(1)
async def task_1():
await asyncio.sleep(1)
assert False
async def task_2():
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, lambda: infinite_while())
loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
tasks = asyncio.gather(task_2(), task_1())
try:
loop.run_until_complete(tasks)
except (Exception, KeyboardInterrupt) as e:
print('ERROR', str(e))
exit()
It prints ERROR but does not exit. When manually closed, the program prints the following stack trace:
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "/usr/lib/python3.5/concurrent/futures/thread.py", line 39, in _python_exit
t.join()
File "/usr/lib/python3.5/threading.py", line 1054, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.5/threading.py", line 1070, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
Upvotes: 15
Views: 13934
Reputation: 7468
When an exception is risen in a task, it never propagates to the scope where the task was launched via eventloop, i.e. the loop.run_until_complete(tasks)
call. Think of it, as if the exception is thrown only in the context of your task, and that is the top level scope where you have the chance to handle it, otherwise it will be risen in the "background".
This said, you will never catch an Exception
from the task with this:
try:
loop.run_until_complete(tasks)
except (Exception, KeyboardInterrupt) as e:
print('ERROR', str(e))
exit()
...and that is just how the event loop works. Imagine if you would have a service with several tasks, and one of them would fail, that would stop the whole service.
What you could do is stop the eventloop manually when you catch an exception in task1
, e.g.
async def task_1():
await asyncio.sleep(1)
try:
assert False
except Exception:
# get the eventloop reference somehow
eventloop.stop()
However, this is very dirty and kind of hacky, so I would rather suggest to go with the solution that @D-Von suggested, which is much cleaner and safer.
Upvotes: 7