I have two tasks. When one task raises an error, I wish to restart them both. Is the following the appropriate way to catch an exception propagated by one task, and restart the gather for the two tasks?
import asyncio
async def foo():
while True:
await asyncio.sleep(1)
print("foo")
async def bar():
for _ in range(3):
await asyncio.sleep(1)
print("bar")
raise ValueError
async def main():
while True:
footask = asyncio.create_task(foo())
bartask = asyncio.create_task(bar())
bothtasks = asyncio.gather(footask, bartask)
try:
await bothtasks
except ValueError:
print("caught ValueError")
try:
footask.cancel()
except asyncio.CancelledError:
pass
asyncio.run(main())
Basically asyncio
intentionally doesn't cancel the other tasks in a gather when one task raises an error. So, since I can't think of anything better, I manually cancel the other task(s) with task.cancel()
and handle the asyncio.CancelledError
myself.
I'm just not convinced this is the intended use of the api, insights appreciated.
Edit:-
In the asyncio-3.7 docs it reads
If gather() is cancelled, all submitted awaitables (that have not completed yet) are also cancelled.
But the behaviour I observe when I replace footask.cancel()
with bothtasks.cancel()
is that for every iteration of the while loop, an additional foo
is awaited, i.e. the foo
appears not to be cancelled by cancelling the gather. The output looks something like this:
foo
bar
foo
bar
foo
bar
caught ValueError
foo
foo
bar
foo
foo
bar
foo
foo
bar
caught ValueError
foo
foo
foo
bar
foo
foo
foo
bar
foo
foo
foo
bar
caught ValueError
...