I am trying to communicate to the repl remotely, but I am having problems in binding the Main module used by the repl and the Main module used by the server and I don’t know how to fix it.
I prepared a couple of scripts to reproduce the issue.
Setup
Server side
Create a test_module.jl with the following content:
module MyModule
using Sockets
# -------------------------------
# Server configuration
# -------------------------------
const _HOST = ip"127.0.0.1"
const _PORT = 8765
const _server_running = Ref(true)
# -------------------------------
# Handlers
# -------------------------------
function get_repl_variables(conn, id, params)
println("active_module server: ", Base.active_module())
println("Main objectid server: ", objectid(Main))
println(names(Main, all=true))
end
# -------------------------------
# Client handler
# -------------------------------
function handle_client(conn::TCPSocket, addr)
println("Client connected from $addr")
try
msg = read(conn, 1)
get_repl_variables(conn, nothing, nothing)
catch e
println("Client error: $e")
finally
close(conn)
end
end
# -------------------------------
# Server loop
# -------------------------------
function start_server()
server = listen(_HOST,_PORT)
println("Julia TCP server running on $_HOST:$_PORT")
while _server_running[]
try
conn = accept(server)
handle_client(conn,getpeername(conn))
catch e
println("Accept error: $e")
end
end
println("Server stopped")
end
# -------------------------------
# Run server in background
# -------------------------------
@async start_server()
# End of module
end
The module is quite easy: it create a server that is supposed to interact with the repl.
When a message is received, then the server calls the function get_repl_variables() that shall print some information about the Main module.
Client side
Next, we should send some message to the server and see what happens in the repl.
You can create a send_request.sh script with the following content (the script use zsh, but I think you can just change the shebang to use it with bash or similar).
#!/usr/bin/env zsh
HOST="127.0.0.1"
PORT=8765
# Send just a few bytes to trigger the server
echo -n "ping" | nc $HOST $PORT
Make the above script executable with chmod +x send_request.sh.
Basic test
At this point, run julia -i test_module.jl. This should start the repl and create the server.
Then, open another terminal windows and run ./send_request.sh.
In the repl, you should see the following:
active_module server: Main
Main objectid server: 8699491554302981441
[Symbol("##meta#60"), :Base, :Core, :Main, :MyModule, :eval, :include]
Now, from the repl, run println("active_module repl: ", Base.active_module()) followed by println("Main object id repl: ", objectid(Main)) and you should see the following
active_module repl: Main
Main object id repl: 8699491554302981441
Finally, from the repl run println(names(Main, all=true)) and you should see the following:
[Symbol("##meta#60"), :Base, :Core, :Main, :MyModule, :eval, :include]
So far so good.
Here comes the issue
Now, from the repl, type A = 10, followed by println(names(Main, all=true)). You should see the following:
[Symbol("##meta#60"), :A, :Base, :Core, :Main, :MyModule, :eval, :include]
As you see, A is in the Main scope.
Next, run ./send_request.sh. You get the following:
[Symbol("##meta#60"), :Base, :Core, :Main, :MyModule, :eval, :include]
BOOM! The variable A is not included.
It seems that either the subprocess has its own Main that does not update when the repl Main is updated - which is strange given that both the Main:s have the same object_id and in both cases is the active module - or that the Main used in the server side is somehow frozen or it reads from some cached values.
Interestingly enough, by running MyModule.get_repl_variables(1,2,3) from the repl, you get:
active_module server: Main
Main objectid server: 8699491554302981441
[Symbol("##meta#60"), :A, :Base, :Core, :Main, :MyModule, :eval, :include]
I have no idea why it does not work when you go through the server, and TBH I don’t know if this is a bug or not. Perhaps the repl namespace has some dedicated area that I fail to see and from where I should pick up info instead of using names(Main, all=true) from the server side?
Perhaps from the server I should query something like names(REPLSecretPlace, all=true) to get all the variables defined in the current interactive repl?
Or perhaps I should bind the repl Main with the server Main in some way? If so, I have no idea how to do that and any suggestion would be highly appreciated.