Skip to content

Commit f1cd8bf

Browse files
committed
fixup! Giant refactor to move all state into a Kernel struct
1 parent 47a2230 commit f1cd8bf

File tree

4 files changed

+93
-42
lines changed

4 files changed

+93
-42
lines changed

src/IJulia.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ mutable struct Msg
7373
end
7474
end
7575

76+
mutable struct Comm{target}
77+
id::String
78+
primary::Bool
79+
on_msg::Function
80+
on_close::Function
81+
function (::Type{Comm{target}})(id, primary, on_msg, on_close) where {target}
82+
comm = new{target}(id, primary, on_msg, on_close)
83+
return comm
84+
end
85+
end
86+
7687
@kwdef mutable struct Kernel
7788
verbose::Bool = IJULIA_DEBUG
7889
inited::Bool = false
@@ -84,10 +95,16 @@ end
8495
Out::Dict{Int, Any} = Dict{Int, Any}()
8596
ans::Any = nothing
8697
n::Int = 0
98+
8799
capture_stdout::Bool = true
88100
capture_stderr::Bool = !IJULIA_DEBUG
89101
capture_stdin::Bool = true
90102

103+
# This dict holds a map from CommID to Comm so that we can
104+
# pick out the right Comm object when messages arrive
105+
# from the front-end.
106+
comms = Dict{String, CommManager.Comm}()
107+
91108
postexecute_hooks::Vector{Function} = Function[]
92109
preexecute_hooks::Vector{Function} = Function[]
93110
posterror_hooks::Vector{Function} = Function[]

src/comm_manager.jl

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,25 @@ module CommManager
22

33
using IJulia
44

5-
import IJulia: Msg, uuid4, send_ipython, msg_pub
5+
import IJulia: Msg, uuid4, send_ipython, msg_pub, Comm
66

7-
export Comm, comm_target, msg_comm, send_comm, close_comm,
7+
export comm_target, msg_comm, send_comm, close_comm,
88
register_comm, comm_msg, comm_open, comm_close, comm_info_request
99

10-
mutable struct Comm{target}
11-
id::String
12-
primary::Bool
13-
on_msg::Function
14-
on_close::Function
15-
function (::Type{Comm{target}})(id, primary, on_msg, on_close) where {target}
16-
comm = new{target}(id, primary, on_msg, on_close)
17-
comms[id] = comm
18-
return comm
19-
end
20-
end
21-
22-
# This dict holds a map from CommID to Comm so that we can
23-
# pick out the right Comm object when messages arrive
24-
# from the front-end.
25-
const comms = Dict{String, Comm}()
26-
2710
noop_callback(msg) = nothing
2811
function Comm(target,
2912
id=uuid4(),
3013
primary=true,
3114
on_msg=noop_callback,
3215
on_close=noop_callback;
16+
kernel=IJulia._default_kernel,
3317
data=Dict(),
3418
metadata=Dict())
3519
comm = Comm{Symbol(target)}(id, primary, on_msg, on_close)
3620
if primary
3721
# Request a secondary object be created at the front end
38-
send_ipython(IJulia.publish[],
39-
msg_comm(comm, IJulia.execute_msg, "comm_open",
22+
send_ipython(kernel.publish[], kernel,
23+
msg_comm(comm, kernel.execute_msg, "comm_open",
4024
data, metadata, target_name=string(target)))
4125
end
4226
return comm
@@ -47,10 +31,10 @@ comm_target(comm :: Comm{target}) where {target} = target
4731
function comm_info_request(sock, kernel, msg)
4832
reply = if haskey(msg.content, "target_name")
4933
t = Symbol(msg.content["target_name"])
50-
filter(kv -> comm_target(kv.second) == t, comms)
34+
filter(kv -> comm_target(kv.second) == t, kernel.comms)
5135
else
5236
# reply with all comms.
53-
comms
37+
kernel.comms
5438
end
5539

5640
_comms = Dict{String, Dict{Symbol,Symbol}}()
@@ -75,16 +59,16 @@ function msg_comm(comm::Comm, m::IJulia.Msg, msg_type,
7559
return msg_pub(m, msg_type, content, metadata)
7660
end
7761

78-
function send_comm(comm::Comm, kernel, data::Dict,
79-
metadata::Dict = Dict(); kwargs...)
80-
msg = msg_comm(comm, IJulia.execute_msg, "comm_msg", data,
62+
function send_comm(comm::Comm, data::Dict,
63+
metadata::Dict = Dict(); kernel=IJulia._default_kernel, kwargs...)
64+
msg = msg_comm(comm, kernel.execute_msg, "comm_msg", data,
8165
metadata; kwargs...)
8266
send_ipython(kernel.publish[], kernel, msg)
8367
end
8468

85-
function close_comm(comm::Comm, kernel, data::Dict = Dict(),
86-
metadata::Dict = Dict(); kwargs...)
87-
msg = msg_comm(comm, IJulia.execute_msg, "comm_close", data,
69+
function close_comm(comm::Comm, data::Dict = Dict(),
70+
metadata::Dict = Dict(); kernel=IJulia._default_kernel, kwargs...)
71+
msg = msg_comm(comm, kernel.execute_msg, "comm_close", data,
8872
metadata; kwargs...)
8973
send_ipython(kernel.publish[], kernel, msg)
9074
end
@@ -97,32 +81,32 @@ end
9781

9882
# handlers for incoming comm_* messages
9983

100-
function comm_open(sock, msg, kernel)
84+
function comm_open(sock, kernel, msg)
10185
if haskey(msg.content, "comm_id")
10286
comm_id = msg.content["comm_id"]
10387
if haskey(msg.content, "target_name")
10488
target = msg.content["target_name"]
10589
if !haskey(msg.content, "data")
10690
msg.content["data"] = Dict()
10791
end
108-
comm = Comm(target, comm_id, false)
92+
comm = Comm(target, comm_id, false; kernel)
10993
invokelatest(register_comm, comm, msg)
110-
comms[comm_id] = comm
94+
kernel.comms[comm_id] = comm
11195
else
11296
# Tear down comm to maintain consistency
11397
# if a target_name is not present
11498
send_ipython(kernel.publish[], kernel,
115-
msg_comm(Comm(:notarget, comm_id),
99+
msg_comm(Comm(:notarget, comm_id; kernel),
116100
msg, "comm_close"))
117101
end
118102
end
119103
end
120104

121-
function comm_msg(sock, msg)
105+
function comm_msg(sock, kernel, msg)
122106
if haskey(msg.content, "comm_id")
123107
comm_id = msg.content["comm_id"]
124-
if haskey(comms, comm_id)
125-
comm = comms[comm_id]
108+
if haskey(kernel.comms, comm_id)
109+
comm = kernel.comms[comm_id]
126110
else
127111
# We don't have that comm open
128112
return
@@ -135,17 +119,17 @@ function comm_msg(sock, msg)
135119
end
136120
end
137121

138-
function comm_close(sock, msg)
122+
function comm_close(sock, kernel, msg)
139123
if haskey(msg.content, "comm_id")
140124
comm_id = msg.content["comm_id"]
141-
comm = comms[comm_id]
125+
comm = kernel.comms[comm_id]
142126

143127
if !haskey(msg.content, "data")
144128
msg.content["data"] = Dict()
145129
end
146130
comm.on_close(msg)
147131

148-
delete!(comms, comm.id)
132+
delete!(kernel.comms, comm.id)
149133
end
150134
end
151135

src/handlers.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ function interrupt_request(socket, kernel, msg)
331331
send_ipython(socket, kernel, msg_reply(msg, "interrupt_reply", Dict()))
332332
end
333333

334-
function unknown_request(socket, msg)
334+
function unknown_request(socket, kernel, msg)
335335
@vprintln("UNKNOWN MESSAGE TYPE $(msg.header["msg_type"])")
336336
end
337337

test/kernel.jl

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,19 @@ inspect(client, code) = make_request(client.inspect, client.get_shell_msg; code)
9595
get_stdin_msg(client) = make_request(Returns(nothing), client.get_stdin_msg)
9696
get_iopub_msg(client) = make_request(Returns(nothing), client.get_iopub_msg)
9797

98-
function get_execute_result(client)
98+
function get_iopub_msgtype(client, msg_type)
9999
while true
100100
msg = get_iopub_msg(client)
101-
if msg["header"]["msg_type"] == "execute_result"
101+
if msg["header"]["msg_type"] == msg_type
102102
return msg
103103
end
104104
end
105105
end
106106

107+
get_execute_result(client) = get_iopub_msgtype(client, "execute_result")
108+
get_comm_close(client) = get_iopub_msgtype(client, "comm_close")
109+
get_comm_msg(client) = get_iopub_msgtype(client, "comm_msg")
110+
107111
function msg_ok(msg)
108112
ok = msg["content"]["status"] == "ok"
109113
if !ok
@@ -190,6 +194,52 @@ end
190194
shutdown_called = false
191195
Kernel(profile; capture_stdout=false, capture_stderr=false, shutdown=() -> shutdown_called = true) do kernel
192196
jupyter_client(profile) do client
197+
@testset "Comms" begin
198+
# Try opening a Comm without a target_name, which should
199+
# only trigger a comm_close message.
200+
open_msg = IJulia.Msg(["foo"],
201+
Dict("username" => "user",
202+
"session" => "session1"),
203+
Dict("comm_id" => "foo",
204+
"data" => Dict()))
205+
IJulia.comm_open(kernel.requests[], kernel, open_msg)
206+
@test isempty(kernel.comms)
207+
@test get_comm_close(client)["content"]["comm_id"] == "foo"
208+
209+
# Setting the target_name should cause the Comm to be created
210+
open_msg.content["target_name"] = "foo"
211+
IJulia.comm_open(kernel.requests[], kernel, open_msg)
212+
@test kernel.comms["foo"] isa IJulia.Comm{:foo}
213+
214+
@test haskey(comm_info(client)["content"]["comms"], "foo")
215+
216+
# Smoke test for comm_msg (incoming to the kernel)
217+
msg_msg = IJulia.Msg(["foo"],
218+
Dict("username" => "user",
219+
"session" => "session1"),
220+
Dict("comm_id" => "foo",
221+
"data" => Dict()))
222+
IJulia.comm_msg(kernel.requests[], kernel, msg_msg)
223+
224+
# Test comm_msg (outgoing from the kernel)
225+
IJulia.send_comm(kernel.comms["foo"], Dict(1 => 2))
226+
@test get_comm_msg(client)["content"]["data"]["1"] == 2
227+
228+
# Test comm_close (outgoing from the kernel)
229+
IJulia.close_comm(kernel.comms["foo"])
230+
# Should this also delete the Comm from kernel.comms?
231+
@test get_comm_close(client)["content"]["comm_id"] == "foo"
232+
233+
# Test comm_close (incoming to the kernel)
234+
close_msg = IJulia.Msg(["foo"],
235+
Dict("username" => "user",
236+
"session" => "session1"),
237+
Dict("comm_id" => "foo",
238+
"data" => Dict()))
239+
IJulia.comm_close(kernel.requests[], kernel, close_msg)
240+
@test !haskey(kernel.comms, "foo")
241+
end
242+
193243
# Test load()/load_string()
194244
mktemp() do path, _
195245
write(path, "42")

0 commit comments

Comments
 (0)