Control Python AsyncIO Coroutine Interactively
Introduction
Python asyncio
concurrency are very good for I/O-bound tasks in Python with less overhead compared to threading
methods. In some rare cases, since Python is a scripting language, we would like to run asyncio
concurrency interactively in REPL (read-eval-print loop), the Python interactive shell. Because Python asyncio
concurrency are single-process and single-thread, while they are being executed, we could not do anything in REPL but wait.
In this blog post, I would like to discuss how to use threading
to control asyncio
concurrency interactively.
AsyncIO Concurrency
AsyncIO Base
1 | import asyncio |
If running this script in the REPL, we would have to wait three seconds after executing results = asyncio.run(gather_func())
before we could execute print(results)
. In some use cases, if it is requesting something from a remote server, it might have a chance to take forever, and we have to press Ctrl
+ C
to kill.
AsyncIO Timeout
If we don’t like waiting too long for the asyncio
concurrency, we have options to timeout.
1 | import asyncio |
If running this script in the REPL, even if we used asyncio.wait_for
with timeout
, we could not do anything but wait during the asyncio
concurrency. In addition, we have to set a fixed timeout
value before we start the asyncio
concurrency. We could not change our mind during waiting, unless we press Ctrl
+ C
to kill.
AsyncIO + Thread
If we have two Python threads, one thread is responsible for the asyncio
event loop which is take care of the concurrency schedules at the low-level, and the other one thread is responsible for the user interactive activities while still having control access to the asyncio
concurrency.
1 | import asyncio |
Now, we could check and cancel asyncio
concurrency anytime during its execution. However, there are a drawback of this method. The concurrent.futures.Future
represents the status of coroutine running in the event loop. The coroutine might contain many tasks, but we will not be able to have access to any of them individually in another thread.
To overcome this, instead of merging multiple tasks into one single task, we could call run_coroutine_threadsafe
multiple times for individual coroutine
s and get multiple future
s to control them individually.
1 | import asyncio |
Object Oriented Programming Version
Please see Ron Frederick’s implementation in our discussion on GitHub.
Acknowledgement
I would like to thank Ron Frederick for the active responses to my questions and the constructive feedbacks.
References
Control Python AsyncIO Coroutine Interactively
https://leimao.github.io/blog/Control-Python-AsyncIO-Coroutine-Interactively/