Search code examples
pythonflasklocalhostbokeh

flask bokeh server - figure does not render even locally


I can't run bokeh server within flask beind apache so now I'm trying to serve bokeh within flask locally. The figure does not render. Here is the flask code:

from flask import Flask, render_template
app = Flask(__name__)

from bokeh.embed import server_document
@app.route("/")
def techblog():
    try:
        tag = server_document(url=r'/bokeh', relative_urls=True)
        return render_template('techblog.html', tag=tag)
    except Exception as e:
        return str(e)

if __name__ == '__main__':
    app.run(debug=True)

Here is the bokeh code:

from numpy.random import random
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.layouts import column, widgetbox
from bokeh.models import Button, ColumnDataSource
from bokeh.server.server import Server

def run(doc):

    fig = figure(title='random data', width=400, height=200, tools='pan,box_zoom,reset,save')

    source = ColumnDataSource(data={'x': [], 'y': []})
    fig.line('x', 'y', source=source)

    def click(n=100):
        source.data = {'x': range(n), 'y': random(n)}

    button = Button(label='update', button_type='success')
    button.on_click(click)

    layout = column(widgetbox(button), fig)
    doc.add_root(layout)
    click()

# configure and run bokeh server
kws = {'port': 5000, 'prefix':'/bokeh','allow_websocket_origin': ['127.0.0.1']}
server = Server(run, **kws)
server.start()
# if __name__ == '__main__':
server.io_loop.add_callback(server.show, '/')
server.io_loop.start()

Here is my html template:

<h1 style='color:blue'>Hello There!</h1>

</br>
{{ tag|safe }}
</br>
{{ tag }}

I'm running flask app via python. And on a separate command processor I run bokeh app via,

bokeh serve --allow-websocket-origin=localhost:5000 filename.py

I only get the tag without "safe" as

<script src="/bokeh/autoload.js?bokeh-autoload-element=1001&bokeh-app-path=/bokeh" id="1001"></script>

And I have this message on flask console. It is a standart 404:

"GET /bokeh/autoload.js?bokeh-autoload-element=1000&bokeh-app-path=/bokeh HTTP/1.1" 404 -

That'a all. No figure or button is rendered. What should I change to see the figure?

Edit: I've specified the port and the prefix in the bokeh code. The outcome has not changed.

Edit 2: I've add the console msj for 404 error on flask console.


Solution

  • I add some details to @bigreddot's answer.


    Code didn't work for me too. I even found exactly this code in some tutorial and main problem was that it was using nginx which was converting /bokeh to http://127.0.0.1/bokeh. But on local computer without nginx I have to change all urls.

    EDIT: I found tutorial with this code https://rbtechblog.com/blog/deploy_bokeh_app


    I start changing code and reduce it to create minimal code which works. I made changes similar to changes mentioned by bigreddot.


    Bokeh

    I put code directly in file without def and without Server

    filename.py

    from numpy.random import random
    from bokeh.io import curdoc
    from bokeh.plotting import figure
    from bokeh.layouts import column, widgetbox
    from bokeh.models import Button, ColumnDataSource
    
    def click(n=100):
        source.data = {'x': range(n), 'y': random(n)}
    
    fig = figure(title='random data', width=800, height=400, tools='pan,box_zoom,reset,save')
    
    source = ColumnDataSource(data={'x': [], 'y': []}) # place for data
    fig.line('x', 'y', source=source)  # draw plot
    
    button = Button(label='update', button_type='success') # create button
    button.on_click(click) # assign function to button
    
    layout = column(fig, widgetbox(button)) # create layout
    curdoc().add_root(layout) # add all to document
    
    click() # generate random data at start
    

    Now I can run it in console

     bokeh serve filename.py 
    

    and I can see it in web browser using url

     http://localhost:5006/filename
    

    (bokeh should display this url in console after start - if you will use different file or options then you may see different url)

    At this moment I don't need any other options but later I will need --allow-websocket-origin but I will describe it later.

    BTW: I not use name bokeh.py because it can make problem to import original bokeh.


    Flask

    Because I don't use nginx which could convert /bokeh to http://localhost:5006/filename so I have to use full url in serve_document

    For test I used render_template_string instead of render_template so I don't have to create templates/index.html so it will easier to copy and test code.

    I removed try/except to get more details if there will be error.

    app.py

    from flask import Flask, render_template, render_template_string
    from bokeh.embed import server_document
    
    app = Flask(__name__)
    
    @app.route("/")
    def index():
        tag = server_document(url='http://localhost:5006/filename')
        #return render_template('index.html', tag=tag)
        return render_template_string('''<div>{{ tag|safe }}</div>''', tag=tag)
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    Now I can run it

    python app.py
    

    and I can open page in web browser using standard url

    http://localhost:5000/
    

    but I will not see plot and bokeh will display

    Refusing websocket connection from Origin 'http://127.0.0.1:5000'; 
    use --allow-websocket-origin=127.0.0.1:5000 
    or set BOKEH_ALLOW_WS_ORIGIN=127.0.0.1:5000 to permit this; 
    currently we allow origins {'localhost:5006'}
    

    so I have to restart bokeh with this option

    bokeh serve filename.py --allow-websocket-origin=127.0.0.1:5000
    

    (as bigreddot mentioned it has to be 127.0.0.1, not localhost)

    And now flask should display plot.

    BTW: if I use template without any HTML tag

    render_template_string('''{{ tag|safe }}''', tag=tag) 
    

    then browser may treat all code (<script ...></scrip>) as part of <head></head> and it will not display it because browser never display elements which are in <head></head> even if there are correct images or plots.