Bokeh server restart on interactive python session

I’m trying to use bokeh from an interactive python session. My idea is to follow these steps:
1.- modify data/visualization
2.- start bokeh server
3.- explore data
4 .- kill server (C-c)
5.- repeat

My code is something like this:

import pandas as pd
import bokeh
import bokeh.io as bio
import bokeh.layouts as blo
import bokeh.plotting as bpl
import bokeh.models as bmo
import bokeh.server.server as bss
from bokeh.server.server import Server

def bkapp(doc):
    df= pd.read_pickle('df.pkl')
    source = bpl.ColumnDataSource(df)

    def callback(attr, old, new):
        metric = sel.value
        data = df[df['metric']==metric]
        source.data = bpl.ColumnDataSource.from_df(data)

    metrics = sorted(df['metric'].unique())
    sel = bmo.Select(title="Metrics:", options=metrics, value=metrics[0])
    sel.on_change("value", callback)

    plot1 = bpl.figure()
    plot1.line('x', 'y', source=source)
    doc.add_root(blo.column(sel, plot1))

server = bss.Server({'/': bkapp}, port=8080, address="0.0.0.0")
server.start()
server.io_loop.start()

It kind of works, but when I start the sever for a second time after making changes to bkapp, it does not update the view. It keeps using the original version of bkapp. Is this the intended behavior? Is there any cache I can clean so the page reflects the changes made to bkapp?

Thank you!

In order for the the view in the browser to update, the browser needs to request a new session (i.e. the browser needs a page reload). The bokeh serve command line command has a --dev option:

 bokeh serve --dev --show app.py

that will automatically cause a page reload when app.py is changed. But you would need to refashion your code in to a plain Bokeh server app module to be run with bokeh serve, instead of using Server programmatically to take advantage of that.

I’m restarting the server and reloading the page each time. What I mean with “it does not update the view” is that the function “bkapp” seems to get stuck on the earliest version used. Lets say I add another selector to the page. It will not be shown on successive servers restarts unless I restart the whole interpreter.

I’d like to keep my intended workflow if possible.

@olvar I thought you were asking about automatic “hot reloading”. If you stop the server, edit the app, restart, and reload the page then you should definitely see any changes you made in between stopping and starting. That’s never not been the case for me in ~5 years, so you’d need to be more specific about exactly the steps you are taking, or perhaps create a video screen grab to demonstrate exactly what you are seeing.

Well, maybe I understand now. You would really need to restart the interpreter. I won’t say it is impossible to accomplish something without restarting, but it was not designed with this in mind, and would be atypical, undocumented usage. You would need to blow away all the old server and app in some way and start a completely new one in the running interpreter, which is going to be tedious. YMMV

Ok, I see. I’ll try to find a way to complete delete the old server each time. Do you have an idea where would be a reasonable starting point to look for in the Bokeh codebase?

Thank you very much!

At a bare minimum you at least need to recreate a new Server configured with new version of bkapp:

server = bss.Server({'/': bkapp}, port=8080, address="0.0.0.0")

If all you do isdefine a new bkapp function, then the original Server will know nothing at all about it. It is still configured with whatever version you originally passed in. That’s not a Bokeh thing, that’s just how variables and references work in Python.

For some reason creating another server does not work for me. What seems to work tho is wrapping the bkapp inside a lambda. I’m still guessing there is some caching going on somewhere.

In case anyone stumbles with this in the future

server = bss.Server({'/': lambda x: bkapp(x)}, port=8080, address="0.0.0.0")

works as intended.
Another option is to fork and start the server on the child. This has the benefit of not blocking the main interpreter.

Thank you for your help!