Configuration of bokeh + channels integration

Hello,

I’m using the bokeh django-embed example and have a few questions about what these lines are doing / how to access them by url:

document(“shape_viewer”, views.shape_viewer_handler),
document(“sea_surface_with_template”, views.sea_surface_handler_with_template)

versus:

autoload("sea_surface", views.sea_surface_handler)

As best as I can tell, if you make the autoload route, then it uses the autoloadjs consumer to bundle up the bokeh app with autoload.js and then the websocket consumer later serves it to the websocket connection? I’m lost about the DocConsumer though - also, for testing purposes I attempted to remove the:

'websocket': AuthMiddlewareStack(URLRouter(patterns))

from routing.py, however for some reason it expects to find a ‘sea_surface/ws’ route - where is this specified? Is it possible to change it to for instance look for the websocket at ‘sea_surface/wasdf’?

Lastly, and I understand I may have to ask this elsewhere, is it possible to do other things with the websocket connection to a session / can I access it anywhere?

Best,
Ryan

I’m very familiar with the design and architecture of the traditional Tornado-based Bokeh server. Not so much the new Django work but I will answer what I can. Perhaps @mateusz can drop in to say more.

  • appname/ws (or for SSL termination, appname/wss) is the standard endpoint that Bokeh adds for the websocket connection it uses to communicate with the browser. It’s never been configurable in the original Bokeh server, and I very much doubt it it is configurable in the new Django work either.

  • The Bokeh websocket protocol receiver will abort the connection as soon as anything unexpected comes over the wire (on either end). While technically probably not impossible to send properly formatted multi-part Bokeh protocol messages yourself, I would not consider this supported usage in any regard.

Hey Bryan,

Thanks for your reply. Yeah I noticed that when I used pull_session and had a consumer sitting at the endpoint I could ‘pretend’ to be the bokeh server, but then I realized I would just be re-implementing the WSConsumer in bokeh.server.django and there was no point in that haha. I think the main thing I’m stuck on is just why is there both an http and ws consumer required (appname/autoload.js for autoload() and appname/ws)? From debugging, it appears that the AutoloadConsumer handles the request first and then sends(?) the bundled version somewhere(?), but then I’m not sure what the websocket is receiving/sending where.

To give a little more context to my questions my use case is:
A highly customizable bokeh plot at for instance localhost/appname, so multiple clients will make a connection, and each receive their own session, at which point they can go about modifying the plot. Let’s say that there are 5 different fields that can all be plotted against each other, over time. So once a client has made a particular graph they’re interested in, they might want to send the details to other connected users. Here is where I would have needed to add a user to a channels group etc.

I’ve wrote a wrapper around the base WSConsumer class but then I had to change the RoutingConfiguration source code to use my consumer:

class Wrapper(WSConsumer):

def __init__(self, scope: Dict[str, Any]):
    super().__init__(scope)

async def connect(self):
    await super().connect()
    print("connecting")

async def disconnect(self, close_code):
    await super().disconnect(close_code)

async def receive(self, text_data):
    await super().receive(text_data)

It successfully connects and displays the graph and additionally prints connecting.

The really nice thing about embedding and why this would be really cool is that I could make use of all the handy bokeh widgets and in my callbacks do things like, share a graph with someone else! Really cool stuff, lots of fun.

Best,
Ryan

Related question and perhaps a little easier to answer:
For using bokeh.server.django autoload you have to (as far as I know) specify the path that it should make itself available to, for instance

autoload("home/x", views.app_handler)

Therefore if I were to want to do something in Django like:

path('home/<str:title>', views.x)

I can’t link it to a bokeh app! Is there any way I can at least pass parameters to the bokeh server in views.x?

It currently looks like:

def x(request: HttpRequest, title) -> HttpResponse:
    script = server_document(request.build_absolute_uri())
    print(request.build_absolute_uri())
    return render(request, "app/embed.html", dict(script=script))

and the views.app_handler looks like:

def app_handler(doc: Document, TitleHereWouldBeGreat!) -> None:
    df = sea_surface_temperature.copy()
    source = ColumnDataSource(data=df)
    plot = figure(x_axis_type="datetime", y_range=(0, 25), y_axis_label="Temperature (Celsius)",
                  title="Sea Surface Temperature at 43.18, -70.43")
    plot.line("time", "temperature", source=source)

    doc.add_root(column(slider, plot))

(taken from examples and simplified)

Ah, I’ve discovered that I can pass in arguments to server_document which are then available on doc however this still requires me to mangle the django request so that it matches what autoload specifies. ie to do:

script = server_document("http://localhost:8000/home", arguments = {"title": title})

Is there a proper way to go about this?

@rmitchel In this case I’d suggest opening a GH issue around this and documentation for the Django integration. I have zero experience with Django so I can’t really comment, we will need to engage with the dev who added this.

Gotcha, will do. I’d be happy to contribute also! Working on a few things but will open it up shortly.

Thanks for the reply,
Ryan

1 Like

Hey @Bryan I’ve created the issue at https://github.com/bokeh/bokeh/issues/9666. If there’s anything I can do to make it clearer please let me know, first bokeh issue created and of course I’d like to see this make progress!