holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.75k stars 517 forks source link

Autoreload opens new tab when launching a server dynamically (pn.serve()) #6339

Open gforti84 opened 8 months ago

gforti84 commented 8 months ago

Software versions: Panel = 1.3.8 bokeh = 3.3.4 python = 3.11.7 OS = Linux Mint 21.2 browser = Firefox 122.0.1

When serving the app dynamically, I've tried to use autoreload = True. It worked in the sense that every code update auto reloads the app. However, it opens a new tab. When using the command to serve my app, this does not happen.

I am using:

pn.serve(panel_app, title="Who am I?", port = 9001, websocket_origin=['*'], autoreload = True, show = True)

philippjfr commented 8 months ago

This isn't really that surprising now that I think about it and I'm not sure practically what can be done about it. What autoreload does is watch the script and re-execute it if it has changed, but if the script runs pn.serve then re-executing it will once again start a server.

philippjfr commented 8 months ago

May have to close this as wontfix unfortunately as it is quite unclear how autoreload should even work in this context. The only reasonable thing that might be supported is:

pn.serve(path_to_app, title="Who am I?", port = 9001, websocket_origin=['*'], autoreload = True, show = True)
gforti84 commented 8 months ago

What do you mean by 'path_to_app'?

MarcSkovMadsen commented 8 months ago

path_to_app is what you want to serve. For example a function returning a Panel component. I believe (?) it can also be a path to a file. That is why its called path_to_app.

kdheepak commented 2 months ago

What do you recommend users should do if they have files like this:

./my_python_package/app.py:

from . import other_module

class App(pn.viewable.Viewer):
    def __panel__(self):
        return ...

./my_python_package/cli.py:

@click.command
def dashboard():
    from . import app
    pn.serve(app.App(), autoreload=True, show=True)

if __name__ == "__main__":
    cli()

The reason I want to structure my project like this is that I can then use the following in a notebook to demo to users:

from my_python_project import app
app.App()

and ship a command line interface for users like

$ my_project_project dashboard

My use case is that I have a complex application that already uses pyproject.toml to manage dependencies and I want to introduce panel incrementally. I would also like to import the components into a notebook to develop them or demo them. I would also like to panel serve my_python_project:app::App --autoreload --show to develop in non-jupyter environments. And I would like this to be user friendly for the rest of the team as well as I introduce panel to the team.

I understand that panel serve filename.py --autoreload --show works. I can make a separate folder outside the python project called ./scripts/app.py with the following content:

from my_python_project import app
app.App().servable()

However that is rather clunky and would like to not do that because I expect I'll have multiple dashboards / components.

Is there a workaround for this?

hoxbro commented 2 months ago

You could use pn.state.served:


if pn.state.served:
    app.App().servable()
kdheepak commented 2 months ago

Sorry, can you clarify? Where would I put those lines?

hoxbro commented 2 months ago

Files where you want to serve the app, something like this:

if __name__ == "__main__":
    cli()
elif pn.state.served:
    from . import app
    app.App().servable()