How To Properly Terminate Nested Asyncio-tasks
I here asked how I can make a version of asyncio.gather that executes the list of tasks sequentially and not in parallel, and good people told me how. But my joy ended as soon as I
Solution 1:
The cancelling try-catch pattern is clunky. Firstly, we want to know about the RuntimeException but instead we're looking at the CancelledError, which is annoying. Secondly, think Futures think promises - don't go round creating Futures you might not follow up on. You wouldn't break a promise, why a Future?
If we know there is a chance we might change out mind, instead of passing around built and ready (even expecting to be) run Futures, pass around coro signatures that can be build JIT (Just In Time) instead. I have the full code below for you, but the take home is that you should pass round the type Callable[[], Coroutine]
instead of just Coroutine
, then build the coro just as you need it.
import asyncio
from typing import Coroutine, Callable
async def work_a():
print("Work 'A' start")
await asyncio.sleep(0)
print("Work 'A' finish")
async def work_b():
print("Work 'B' start")
await asyncio.sleep(0)
print("Work 'B' finish")
async def raise_exception():
print("raise_exception executed")
await asyncio.sleep(0)
raise RuntimeError("raise_exception executed")
def in_sequence(*arg_tasks: Callable[[], Coroutine]) -> Callable[[], Coroutine]: # <-- New signature
async def coro():
"""Executes coroutines in sequence"""
tasks = iter(arg_tasks)
try:
for task in tasks:
await task() # <-- create JIT# Terminates all other tasks scheduled for execution in event loop to prevent "RuntimeWarning: coroutine was never awaited" messagefinally:
for remaining_task in tasks:
pass
# remaining_task.close() # <-- Not needed nowreturn coro
async def main():
try:
await in_sequence( # outer in_sequence()
work_a,
raise_exception,
in_sequence( # inner in_sequence()
work_b
)
)() #<-- Remember to call to build coro
except Exceptionas e:
print(f"+++ Quit with exception: {e}")
if __name__ == '__main__':
asyncio.run(main())
Outputs:
Work 'A'start
Work 'A' finish
raise_exception executed
+++ Quit with exception: raise_exception executed
Process finished with exit code 0
Post a Comment for "How To Properly Terminate Nested Asyncio-tasks"