You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add guest mode to asyncio, enabling it to run cooperatively inside a host event loop such as Tkinter, Qt, GTK, or any other framework that owns the main thread.
Motivation
GUI toolkits (Tk, Qt, GTK, pygame, ...) each have their own event loop that must run on the main thread. Today there is no standard way to use asyncio coroutines inside a GUI application without:
Polling hacks (periodic root.after() calls)
Replacing the event loop entirely (fragile, framework-specific)
Running asyncio in a separate thread and manually marshalling results
Trio solved this problem with trio.lowlevel.start_guest_run() and the approach has proven robust across many GUI frameworks (see trio-guest).
The implementation decomposes BaseEventLoop._run_once() into three new public methods — poll_events(), process_events(), and process_ready() — then uses a dual-thread architecture where the host thread processes callbacks and a daemon I/O thread polls for events.
Note that there is already a guest mode inspired by Trio: oremanj/aioguest. There is also my gist, which works without greenlets (and which I am currently refactoring/improving/etc. to support Curio and release as a standalone package). If you want to implement a reliable guest mode, you should also consider related issues such as #85093 (and also handle the wakeup fd, otherwise you will have issues with signal handlers).
The implementation decomposes BaseEventLoop._run_once() into three new public methods — poll_events(), process_events(), and process_ready() — then uses a dual-thread architecture where the host thread processes callbacks and a daemon I/O thread polls for events.
I believe that this is insufficient, especially in terms of performance. Let us suppose we have a set of tasks, and at least one of them is active at any point in time. In this case, the timeout will always be zero. But since in your model, events are always polled by a separate thread, even a zero timeout (even with a single task that simply does asyncio.sleep(0) in an infinite loop) will result in quite expensive ping-ponging between the two threads, and we will also lose determinism between guest tasks and host tasks.
The problem can be solved by providing a method for obtaining an effective timeout and delegating polling to a separate thread only when it is non-zero. The entire poll_events() code before the select() call is responsible for this.
However, these methods are too low-level. Any guest mode implementation will have to repeat almost the entire asyncio.Runner.run() (both thread-local state management and event loop shutdown; I do not see the latter or even loop._run_forever_setup()/loop._run_forever_cleanup() in the linked PR).
Add guest mode to asyncio, enabling it to run cooperatively inside a host event loop such as Tkinter, Qt, GTK, or any other framework that owns the main thread.
The feature request and the thread on discuss.python.org both emphasize the usefulness of an asyncio guest mode in the context of GUI applications. I'd like to highlight the benefits for another use case, which is application testing.
For example, a developer of an ASGI web application may want to start a web server as part of their test suite and issue requests against it. These requests would typically be distributed over several test cases. Since there can only be one async event loop per thread, the same event loop running the web server will also have to run the test cases. This weakens the isolation between test cases and can lead to side effects through the shared event loop. Starting the webserver in a dedicated thread or process instead of a shared loop can introduce a lot of complexity (mixed concurrency approaches, signal handling, …) or may not be an option at all due to the lack of thread-safety of the test runner (e.g. pytest).
In this example, the proposed asyncio guest mode would enable stronger isolation between individual test cases and between tests and the web server. Other approaches, such as patching an asyncio event loop with the nest_asyncio package, are not able to provide the same guarantees.
Activity
pythongh-145342: asyncio: Add guest mode for running inside external …
pythongh-145342: asyncio: Add docs and NEWS entry for guest mode
x42005e1f commentedon Mar 4, 2026
Note that there is already a guest mode inspired by Trio: oremanj/aioguest. There is also my gist, which works without greenlets (and which I am currently refactoring/improving/etc. to support Curio and release as a standalone package). If you want to implement a reliable guest mode, you should also consider related issues such as #85093 (and also handle the wakeup fd, otherwise you will have issues with signal handlers).
x42005e1f commentedon Mar 4, 2026
I believe that this is insufficient, especially in terms of performance. Let us suppose we have a set of tasks, and at least one of them is active at any point in time. In this case, the timeout will always be zero. But since in your model, events are always polled by a separate thread, even a zero timeout (even with a single task that simply does
asyncio.sleep(0)in an infinite loop) will result in quite expensive ping-ponging between the two threads, and we will also lose determinism between guest tasks and host tasks.The problem can be solved by providing a method for obtaining an effective timeout and delegating polling to a separate thread only when it is non-zero. The entire
poll_events()code before theselect()call is responsible for this.However, these methods are too low-level. Any guest mode implementation will have to repeat almost the entire
asyncio.Runner.run()(both thread-local state management and event loop shutdown; I do not see the latter or evenloop._run_forever_setup()/loop._run_forever_cleanup()in the linked PR).seifertm commentedon May 15, 2026
The feature request and the thread on discuss.python.org both emphasize the usefulness of an asyncio guest mode in the context of GUI applications. I'd like to highlight the benefits for another use case, which is application testing.
For example, a developer of an ASGI web application may want to start a web server as part of their test suite and issue requests against it. These requests would typically be distributed over several test cases. Since there can only be one async event loop per thread, the same event loop running the web server will also have to run the test cases. This weakens the isolation between test cases and can lead to side effects through the shared event loop. Starting the webserver in a dedicated thread or process instead of a shared loop can introduce a lot of complexity (mixed concurrency approaches, signal handling, …) or may not be an option at all due to the lack of thread-safety of the test runner (e.g. pytest).
In this example, the proposed asyncio guest mode would enable stronger isolation between individual test cases and between tests and the web server. Other approaches, such as patching an asyncio event loop with the nest_asyncio package, are not able to provide the same guarantees.
pythongh-145342: Add _guest_mode flag and guest-mode signal-handler g…
pythongh-145342: Make the guest I/O thread non-daemonic with graceful…
pythongh-145342: Expand asyncio guest mode tests
pythongh-145342: Update asyncio guest mode docs and NEWS
pythongh-145342: Adapt guest mode to current main and fix docs CI
pythongh-145342: Reference the asyncio-guest example collection in th…
pythongh-145342: Add _guest_mode flag and guest-mode signal-handler g…
pythongh-145342: Make the guest I/O thread non-daemonic with graceful…
pythongh-145342: Expand asyncio guest mode tests
pythongh-145342: Update asyncio guest mode docs and NEWS
pythongh-145342: Adapt guest mode to current main and fix docs CI
pythongh-145342: Reference the asyncio-guest example collection in th…