Dash.jl
Dash.jl copied to clipboard
Add option to run a non-blocking server
Heya, The blocking nature of run_server makes this difficult to use from the REPL. REPL use seems to be the recommended way of getting around many of Julia's pain points, so it would be great for this to be easy.
Would you accept a PR to basically make start_server a public part of the API again?
Cheers
Hi @akdor1154!
start_server is the internal lambda function and it has never been public. You can use make_handler to get an HTTP-compatible handler and then use HTTP.serve to manually start the server in synchronous or asynchronous mode.
handler = make_handler(app);
@async HTTP.serve(handler, host, port)
In this case, to enable debugging mode, you can use enable_dev_tools!(app; debug, dev_tools_ui, ...)
Just look at how run_server works (https://github.com/plotly/Dash.jl/blob/dev/src/server.jl). make_handler is part of a public API of Dash. In addition to asynchronous server startup, you can use it to build your own handler - for example, to add authorization or any other pre/post processing.
yep for sure. It's just that it's really useful to have a non-blocking version of run_server readily available, and start_server fulfils this perfectly (I've copy/pasted it out of Dash.jl to use in my start script). If you want to keep it private then that's fine I guess, but from a user perspective it would be helpful to have access to this.
For some context, this is what I've built with it:
# this is the bit I am trying to avoid reimplementing :)
module StartServer
import Dash
import HTTP
import Sockets
import Sockets: IPAddr, TCPServer
struct CurrentServer
server :: TCPServer
task :: Task
end
export CurrentServer
get_inetaddr(host::String, port::Integer) = Sockets.InetAddr(parse(IPAddr, host), port)
get_inetaddr(host::IPAddr, port::Integer) = Sockets.InetAddr(host, port)
function start_server(app::Dash.DashApp, host::Union{String, IPAddr} = Sockets.localhost, port::Integer = 8080) :: CurrentServer
handler = Dash.make_handler(app);
server = Sockets.listen(get_inetaddr(host, port))
task = @async HTTP.serve(handler, host, port; server = server)
@info string("Running on http://", host, ":", port)
return CurrentServer(server, task)
end
export start_server
end
# once the above is available then you can use it with Revise like
using Revise
import MyDashProject
module RunRepl
import ..MyDashProject
using ..StartServer
struct ServerState
currentServer::CurrentServer
end
export ServerState
function reload(state::Union{ServerState, Nothing}) :: ServerState
if state !== nothing
println("closing server...")
close(state.currentServer.server)
end
app = MyDashProject.buildApp()
newServer = RunRepl.start_server(app)
return ServerState(newServer)
end
export reload
end
function reload()
global serverState
Revise.revise()
serverState = RunRepl.reload(serverState)
return nothing
end
serverState = RunRepl.reload(nothing)
And do your dev work with a full REPL available by calling
julia --project=@. -i ./runRepl.jl
>reload() runs Revise() and restarts your server on demand, quicker than builtin hot reload.
Well. I'll think about it a little more. There are 2 options
- I will include it in one of the future PRs (however, the General approach of plotly is that the interface should be as simple as possible and close to what is available in other languages)
- I will soon start writing a package like
DashHelpersorDashMeta, which will contain all sorts of utilities and macros for advanced work withDash
Most likely there will be a second option. Among other things, it will allow us to write things specific and convenient for Julia without regard to the compatibility of interfaces and documentation with Python and without reviews from the Plotly Dash team. In other words, it will be a package for that the Julia community responsible, not Plotly Dash
Sure. I might submit a quick PR tomorrow (sorry, dinner time :)) with the sort of thing I'd be after, but with no expectation of it being merged. Cheers, and thanks for your work on this.
@akdor1154 This is a great suggestion; in Dash for R we provide the functionality you're asking about via run_server. All that's required is to pass block = FALSE.
@alexcjohnson Would it make sense to add non-blocking support to the Dash.jl roadmap, controlled via an argument to run_server here as well?
@alexcjohnson Yes, of course it is possible and not difficult. The only issue that confused me was the complexity of the interface for beginners.