Bokeh server/client session initialization

Hi, I am exploring different mechanisms to provide user data to a bokeh application run via bokeh serve. In the initial use-case being considered, I might be able to do everything I need by passing a modest amount of data during the initialization step, i.e.when a client session is started, and I’d like to explore the flexibility and features of that before going to something more heavyweight like pulling in database connections, etc.

The bokeh documentation here https://docs.bokeh.org/en/latest/docs/reference/client/session.html#use-cases indicates that a ClientSession can provide data via an option arguments dictionary.

However, I have not successfully found through the documentation how to access that data in the server? Is it some specific attribute of the document, session_context, etc? Is this best handled in the on_session_created() method in a server_lifecycle.py file?

And, as a side question, are there any known limitations or drawbacks to using the Python client implementation?

Thanks

Update

A web search revealed a similar topic on stackoverflow here https://stackoverflow.com/questions/57420450/how-can-a-bokeh-server-access-arguments-from-server-document

with a reference to the bokeh documentation to access the HTTP request here https://docs.bokeh.org/en/latest/docs/user_guide/server.html#accessing-the-http-request

The following method works as expected, entering in the web browser for a server app named bkex

http://localhost:5006/bkex?N=10

However, an attempt to do this programmatically fails to return the expected arguments. I get an empty dictionary when inspecting the document.session_context.request.arguments on the server. I used the following Python client code to instantiate and run a client quickly. I am sure its a misunderstanding on my end about how to use this client API.

from bokeh.client import ClientSession

arguments =  {'N': [b'10']}
S = ClientSession(websocket_url='ws://localhost:5006/ws', arguments=arguments)
S.show()

My setup for running the Python client was Spyder console (v4.1.2) through Anaconda. The bokeh version is 2.0.1

It’s a little unclear what the situation is. Is this just you interacting with the Bokeh app, locally? Then sure, that’s probably OK. if this is something you need to do for other viewers and you want to “bake in” customizations per-session, then typically (in a Flask app, say) you would call pull_session to get a session from the Bokeh server, and then use server_session to embed that customized session in a rendered HTML page.

Hi @Bryan

Thanks for the quick and informative reply.

I am initially trying to interact locally to get an idea of how I conceptually want to architect the client/server data sharing. The method I tried in the client code snippet did not show any of the session-context request data on the server side, whereas appending the parameters to the URL and going through the browser directly does show the expected parameters.

@_jm I guess more info is needed. If I run the following Bokeh app:

from bokeh.io import curdoc
from bokeh.models import Div

text = f"ARGS: {repr(curdoc().session_context.request.arguments)}"
print (text)
curdoc().add_root(Div(text=text))

And then connect to it with your ClientSession code above, I see the ARGS printout in the server log and in the Div that the app displays.

Hey @Bryan

Thanks again for the engagement.

I am running as a bokeh server with the directory structure format. My main.py program, with the non-essential pieces removed, follows.

main.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
"""
from bokeh.plotting import curdoc
from bokeh.models import Button

D = curdoc()

def click_cb():
    arg = D.session_context.request.arguments
    print("SESSION REQUEST {:}".format(arg))

b = Button()
b.on_click(click_cb)

D.add_root(b)

If I access this with parameters appended to the end of the URL in a web-browser, I see the arguments printed to my server’s terminal window when pressing the pushbutton.

If I use the simple client code earlier in this thread, I do not. I see the following …

2020-05-05 12:29:48,536 Starting Bokeh server version 2.0.1 (running on Tornado 6.0.4)

2020-05-05 12:29:48,539 User authentication hooks NOT provided (default user enabled)

2020-05-05 12:29:48,543 Bokeh app running at: http://localhost:5006/bkex

2020-05-05 12:29:48,543 Starting Bokeh server with process id: 63417

2020-05-05 12:30:13,825 WebSocket connection opened

2020-05-05 12:30:13,825 ServerConnection created

SESSION REQUEST {}

There is a couple of different issues going on I think, I will have to dig more in to it when I have time. But I will say these now:

  • Your arguments dict has the wrong format. You should pass a dict that maps strings to strings, and nothing else:

    arguments =  {'N': '10'}
    

    The weird type with a list as the value when reading the arguments, is an artifact of how Tornado handles request arguments, which can be repeated in the URL string. But Bokeh APIs just expect Dict[str, str] when setting them.

  • I did not mention but ClientConnection is actually extremely low level API. I should have pointed you at pull_session which is actually what is intended as user facing. I think there are some subtleties at that level.

  • I think you should use the real full app URL when dealing explicitly with clients and sessions. I am not sure the index handler for / is vetted for this usage where you connect directly to the WS, and there may be subtleties there as well.

  • Lastly, tho, when using the full app URL there seems to be an issue loading resources from the server itself when using sess.show() Maybe not surprising the show method on sessions is basically never used for anything. For now I mitigated this by using CDN resources.

TLDR; try this:

BOKEH_RESOURCES=cdn bokeh serve app

Then:

from bokeh.client import pull_session

app_url = "http://localhost:5006/app"  # full path

arguments =  {'N': '10'}  # no list value

S = pull_session(url=app_url, arguments=arguments)  # higher level API

S.show()

Thanks @Bryan

Your detailed explanation of what works, doesn’t work, and why is helpful. The suggested workaround addresses my need.