-
-
Notifications
You must be signed in to change notification settings - Fork 367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace Tornado with AnyIO #1079
Conversation
+1 on AnyIO, they seem to have all the missing primitives needed for doing asyncio+threaded work, seems like a solid project. |
As a |
I must point out that using blocking portals in this manner will block the original event loop, preventing any other tasks from running until the blocking call is complete. What should also be taken into account is any Protocols and tasks launched from the nested loop, which will also stop functioning once the blocking call completes. As a former Java programmer, I recall that Java used a similar approach to solve this problem, but there the nested event loop was able to process events from the parent loop. |
Thanks for the feedback @agronholm. |
Yeah, sorry, it's a rather large and complex PR so I haven't read through it, so I just made assumptions based on your other posts. So are you creating one-off event loops for running async callables from sync functions? Or do you have a lingering, shared event loop thread running in the background? Either way, thread safety is the biggest problem. One of the main selling points of async is that you can reliably determine where you can mutate shared objects safely (without explicit locks) since all other code is suspended until the next yield point. Those guarantees go out the window when two simultaneously running event loops share memory.
Technically there is no such thing as an AnyIO event loop 😄 |
Yes, the control thread runs for the whole duration of the kernel process, so we're not creating lots of event loops. |
Just a quick note to say that this seems significant enough to mark it for IPykernel 7. |
Yes, I agree. It also requires |
7b04678
to
3cb5832
Compare
08a3baa
to
b2fe7c1
Compare
I'd like to merge this PR and update downstream projects in other PRs. |
Great, thank you! |
I'm looking into it, but at first sight this code was working in my first commit: from threading import Thread
from time import sleep
def thread_target():
for i in range(5):
print(i, end='', flush=True)
sleep(0.25)
Thread(target=thread_target).start() It successfully prints |
Is |
No, but yes we might replace all this complicated way of communicating between threads using ZMQ pipes with AnyIO's functions. |
Doesn't look good 😄 |
@blink1073 For me even without c1dce80 a lot of tests hang locally, for instance tests related to cell interrupt and in-process kernels. It seemed to be the case in the CI too. |
So currently the remaining failing tests are in |
I'd say let's skip those for now, and then merge and iterate. |
One remaining failing test on Windows: |
Sounds right to me |
Thanks @davidbrochart! |
Thank you @blink1073 ! |
Is a fix for the in-process kernel test failure planned. before the release of v7? |
References
Supersedes #876.
Closes #656.
IPykernel should not depend on a web server. It uses Tornado as an async framework, but modern Python has better solutions, including standard
asyncio
. Tornado's use in ipykernel involves using a lot of queues and creates complexity that makes maintenance or improvements to the code base harder.Structured concurrency as introduced by Trio is gaining a lot of traction. AnyIO brings structured concurrency on top of
asyncio
and acts as an abstraction layer which allows to choose a concurrency backend, such asasyncio
ortrio
. Major projects have switched to AnyIO, including FastAPI.This PR is still a work in progress. I have disabled some tests, like the ones for in-process kernels. Also, I think we should re-think event-loop integration (maybe using Trio's approach). But I would like to get feedback from the community and discuss how we can move forward with these changes.
cc @SylvainCorlay @JohanMabille @minrk @ccordoba12
Code changes
The dependency to Tornado is dropped, in favor of AnyIO. This means that instead of using ZMQStream with
on_recv
callbacks, we use async/await everywhere.Using AnyIO in the main thread event loop gives us Trio support for free in the user code. It is currently disabled until we get AnyIO support in pyzmq, or decide to process shell messages in a separate thread, which might be needed for sub-shells anyway.
User-facing changes
The goal is to have no user-facing change. However, I have identified the following ones:
stdout
,stderr
) is improved as there is no delay anymore.Backwards-incompatible changes
shell_socket
).async
, including theKernel.start
method.Kernel.stop
(thread-safe) method.