-
-
Notifications
You must be signed in to change notification settings - Fork 417
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
Giant refactor to move all state into a Kernel struct #1145
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1145 +/- ##
===========================================
+ Coverage 10.21% 59.61% +49.40%
===========================================
Files 14 14
Lines 822 894 +72
===========================================
+ Hits 84 533 +449
+ Misses 738 361 -377 ☔ View full report in Codecov by Sentry. |
Figured out the deadlock, we can now start writing full tests with |
4aa3633
to
64ec133
Compare
This is the only change in 5.4, in previous versions it was sent on the shell socket.
This is what Jupyter expects.
The Jupyter messaging docs state that all messages on the shell (request) channel shall be ended with a `status: idle` message, and that: "This idle status message indicates that IOPub messages associated with a given request have all been received." Previously we did not attempt to ensure this, which meant that in some cases the `execute_result` message would be published after the `execute_reply` message had been sent. It's still not exactly guaranteed by the `yield()`, but it's something and it makes the `jupyter_kernel_test` tests pass.
This is required by the spec: https://jupyter-client.readthedocs.io/en/latest/messaging.html#introspection
64ec133
to
b19c5aa
Compare
using ZMQ, JSON, SoftGlobalScope | ||
import Base.invokelatest | ||
import Base: invokelatest, RefValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably using
rather than import
since we only use these, not extend them? Also invokelatest
is exported automatically these days.
(This code dates back to the days before using Foo: bar
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but I think I'd prefer to do those sorts of changes in a later PR. This one is already quite large 😅
src/IJulia.jl
Outdated
setfield!(value, name, x) | ||
end | ||
|
||
function Base.close(kernel::Kernel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be called by a finalizer
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately that wouldn't be easy because it wait
's on the stdio tasks so it might yield. I think it's ok to rely on the do-constructor closing it in the tests.
# global counterparts. | ||
if name ∈ (:ans, :n, :In, :Out, :inited, :verbose) | ||
setproperty!(IJulia, name, x) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aren't ans, In, Out
global in Main
(not IJulia
)?
Maybe this kind of global should be copied to/from Main before/after each execute request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whereas n
and verbose
at least can probably be moved in to the Kernel
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're quite right 🤔 I need to figure out a good way to test that, for now I've added a @test_broken
test in d9acb6e.
whereas
n
andverbose
at least can probably be moved in to theKernel
?
n
is documented to be module-global (https://julialang.github.io/IJulia.jl/dev/library/public/#IJulia.n) so I don't think we can move that fully into Kernel
, but verbose
should be possible. I think only @vprintln
/@verror_show
will need tweaking to look at _default_kernel.verbose
instead of the global.
src/stdio.jl
Outdated
@@ -274,8 +260,8 @@ function oslibuv_flush() | |||
end | |||
|
|||
import Base.flush | |||
function flush(io::IJuliaStdio) | |||
function flush(io::IJuliaStdio, kernel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a user-visible function, e.g. if the user calls flush(stdout)
.
It would be better to store a reference to the kernel in the IJuliaStdio
object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, fixed in d9acb6e and added a test for it.
Progress update:
To-do:
|
Pretty much what the title says 😛 This is not ready yet but I'm opening it for transparency. There are two reasons for the refactor:
Overview:
Almost all global state has been moved into the new
Kernel
struct, and all relevant internal methods now require a kernel be passed to them. That's the bulk of the diff. I've tried to minimize other changes as much as possible, so there should 🤞 be no change in behaviour frommaster
.There are certain fields like
ans
andn
which are documented as globals and must stay global, sosetproperty!(::Kernel,...)
is implemented such that it will copy changes to those fields to their corresponding global variable. Similarly there are certain public methods likeIJulia.history()
that must still work without a kernel argument, so a new_default_kernel
global is defined for normal usage and that's used by default for all public methods.An edge-case are the dicts
In
andOut
which are by default assigned to the corresponding fields of_default_kernel
and will not contain changes from temporary test kernels. I don't think that's a particularly important discrepancy so I'm inclined to ignore it.I implemented a basic test in
waitloop.jl
that spawns the IJulia waitloop and then makes a request to it usingjupyter_client
. This isn't working very well right now because blocking calls to the client causes hangs, I suspect there's a deadlock somewhere.This will need a lot more work before it can be merged, but feel free to review the design already (cc @stevengj, @fredrikekre).
@MasonProtter, you should be able to do... something... with this for a precompilation workload. I would suggest creating a
Kernel
, connecting to some of the sockets with ZMQ.jl, and then sending some mock requests following the wire protocol: https://jupyter-client.readthedocs.io/en/latest/messaging.html#the-wire-protocol