Skip to content
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

Add option to run a non-blocking server #48

Open
akdor1154 opened this issue Jul 20, 2020 · 6 comments
Open

Add option to run a non-blocking server #48

akdor1154 opened this issue Jul 20, 2020 · 6 comments

Comments

@akdor1154
Copy link

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

@waralex
Copy link
Collaborator

waralex commented Jul 20, 2020

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.

@akdor1154
Copy link
Author

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.

@waralex
Copy link
Collaborator

waralex commented Jul 20, 2020

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 DashHelpers or DashMeta, which will contain all sorts of utilities and macros for advanced work with Dash

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

@akdor1154
Copy link
Author

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.

@rpkyle
Copy link
Contributor

rpkyle commented Jul 21, 2020

@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?

@waralex
Copy link
Collaborator

waralex commented Jul 21, 2020

@alexcjohnson Yes, of course it is possible and not difficult. The only issue that confused me was the complexity of the interface for beginners.

@rpkyle rpkyle changed the title Difficult to use from REPL Add option to run a non-blocking server Jul 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants