Search code examples
pythonpyramidbokeh

How do I integrate a Bokeh Server into a Pyramids Application?


In order of complexity, with Pyramids, I can create static bokeh graphs and then incorperate them with div tags like outlined here.

The Bokeh documentations explain clearly how one can setup a bokeh server for interactive data exploration, and I have successfully created such an application.

What I would like to do though is to have an interactive graph within a Pyramids view page. The requirements of this page would be as followed:

  • Upon loading of the view, a bokeh server is somehow started, and the data is loaded into the server's object model.
  • Somehow the Pyramid view will also receive the data in the server object model and render the data.

There are things I am not clear about:

  • I am not sure as to where the "widgets" used for selecting and filtering data should be rendered. It appears that for easy interaction with the rest of the graph, they should be part of the bokeh server.
  • I am not sure how to integrate a bokeh server page into a Pyramids view.
  • I am also not sure how one would approach starting of the bokeh server from within a Pyramids web app.

There is one paragraph that mentions how bokeh server can be embedded into either a Flask or Tornado app. But the paragraph is too brief for me to make sense right now. So I am asking how would I do this in Pyramids?


Solution

  • As bigreddot said, the workflow is quite similar with minute changes in the code. I actually built my answer based on his anwer. Thanks bigreddot!

    Following is my solution for integrating bokeh-sever with Pyramid.

    1. Create a function to generate Bokeh document (plot)
    def bokeh_doc(doc):
        # create data source
        # define all elements that are necessary
        # ex: 
        p = line(x, y, source)
    
        # now add 'p' to the doc object
        doc.add_root(p)
    
        # define a callback if necessary
        # and register that callback
        doc.add_periodic_callback(_cb, delay)
    
    1. Add a route location of the app to Pyramid server config object. Mostly in the __init__.py or any other file where you configure the routes.
        conf.add_route('bokeh_app', '/bokeh-app')
    
    1. Add a view where the bokeh_app has to be rendered. This function could be written in views.py or where ever you deem appropriate.
    from pyramid.view import view_config
    from bokeh.embed import server_document
    
    @view_config(route_name='bokeh_app', renderer='static/plot.jinja2')
    def bokeh_view(request):
        # this '/app' route to the plot is configured in step. 4
        # using default host and port of bokeh server. 
        # But, the host and port can be configured (step. 4)
        script = server_document('localhost:5006/app') 
    
        # assuming your jinja2 file has 
        # {{ script|safe }}
        # embedded somewhere in the <body> tag
        return {'script': script}
    
    1. Now, fire-up a bokeh server.
    from bokeh.application import Application 
    from bokeh.application.handlers import FunctionHandler 
    from bokeh.server.server import Server
    
    # bokeh_doc is the function which defines the plot layout (step. 1)
    chart_app = Application(FunctionHandler(bokeh_doc))
    
    # the '/app' path is configured to display the 'chart_app' application
    # here, a different host and port for Bokeh-server could be defined
    # ex: {"<host2:9898>/app_bokeh": chart_app}
    bokeh_server = Server({"/app": chart_app}, allow_websocket_origin=["localhost:6543"]) 
    
    # start the bokeh server and put it in a loop
    server.start()
    server.io_loop.start()
    

    allow_websocket_origin takes in a list of strings that must be upgraded to support web-socket connection required by bokeh. In this case, we need give the url of the pyramid server

    1. Finally, start the Pyramid server
    from wsgiref.simple_server import make_server
    
    pyramid_app = conf.make_wsgi_app()
    pyramid_server = make_server('localhost', 6543, pyramid_app)
    
    pyramid_server.serve_forever()