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.
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?
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.
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.
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()