ISSUE Bokeh server application with moderate size column data source (10000 samples of 100 variables) works when accessed directly via standalone server but fails when embedded in a Flask application using pull_session()/server_session()
mechanism.
It can be shown by example that the problem manifests based on the size of the data source used in a plot even in cases where only two of the columns are used in the rendering.
ENVIRONMENT Mac OSX Big Sur with Anaconda Python. Python 3.8.5, Bokeh 2.2.3, Flask 1.1.2.
Client browser Google Chrome Version 87.0.4280.141 (Official Build) (x86_64)
ERRORS (Flask terminal log)
% python flask_bokeh.py
* Serving Flask app "flask_bokeh" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:8000/ (Press CTRL+C to quit)
ERROR:tornado.application:Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOLoop object at 0x7f966006fd00>>, <Task finished name='Task-12' coro=<ClientConnection._next() done, defined at /Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py:307> exception=TypeError("_transition_to_disconnected() missing 1 required positional argument: 'dis_state'")>)
Traceback (most recent call last):
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 741, in _run_callback
ret = callback()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 765, in _discard_future_result
future.result()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py", line 316, in _next
await self._state.run(self)
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/states.py", line 139, in run
return await connection._transition_to_disconnected()
TypeError: _transition_to_disconnected() missing 1 required positional argument: 'dis_state'
ERROR:tornado.application:Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOLoop object at 0x7f969057c280>>, <Task finished name='Task-18' coro=<ClientConnection._next() done, defined at /Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py:307> exception=TypeError("_transition_to_disconnected() missing 1 required positional argument: 'dis_state'")>)
Traceback (most recent call last):
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 741, in _run_callback
ret = callback()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 765, in _discard_future_result
future.result()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py", line 316, in _next
await self._state.run(self)
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/states.py", line 139, in run
return await connection._transition_to_disconnected()
TypeError: _transition_to_disconnected() missing 1 required positional argument: 'dis_state'
ERROR:tornado.application:Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOLoop object at 0x7f9690582430>>, <Task finished name='Task-27' coro=<ClientConnection._next() done, defined at /Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py:307> exception=TypeError("_transition_to_disconnected() missing 1 required positional argument: 'dis_state'")>)
Traceback (most recent call last):
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 741, in _run_callback
ret = callback()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 765, in _discard_future_result
future.result()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py", line 316, in _next
await self._state.run(self)
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/states.py", line 139, in run
return await connection._transition_to_disconnected()
TypeError: _transition_to_disconnected() missing 1 required positional argument: 'dis_state'
127.0.0.1 - - [22/Jan/2021 08:31:01] "GET / HTTP/1.1" 200 -
INFO:werkzeug:127.0.0.1 - - [22/Jan/2021 08:31:01] "GET / HTTP/1.1" 200 -
ERROR:tornado.application:Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOLoop object at 0x7f9660203310>>, <Task finished name='Task-45' coro=<ClientConnection._next() done, defined at /Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py:307> exception=TypeError("_transition_to_disconnected() missing 1 required positional argument: 'dis_state'")>)
Traceback (most recent call last):
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 741, in _run_callback
ret = callback()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/tornado/ioloop.py", line 765, in _discard_future_result
future.result()
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/connection.py", line 316, in _next
await self._state.run(self)
File "/Users/***/opt/anaconda3/lib/python3.8/site-packages/bokeh/client/states.py", line 139, in run
return await connection._transition_to_disconnected()
TypeError: _transition_to_disconnected() missing 1 required positional argument: 'dis_state'
INFO (client browser JS Console log)
INFO (bokeh server terminal)
% bokeh serve bkapp.py --port=5006 --allow-websocket-origin=localhost:8000 --allow-websocket-origin=localhost:5006 --unused-session-lifetime=60000 --keep-alive=1000
2021-01-22 08:31:18,041 Starting Bokeh server version 2.2.3 (running on Tornado 6.1)
2021-01-22 08:31:18,042 Keep-alive ping configured every 1000 milliseconds
2021-01-22 08:31:18,042 Unused sessions last for 60000 milliseconds
2021-01-22 08:31:18,042 User authentication hooks NOT provided (default user enabled)
2021-01-22 08:31:18,044 Bokeh app running at: http://localhost:5006/bkapp
2021-01-22 08:31:18,044 Starting Bokeh server with process id: 90205
2021-01-22 08:31:21,185 WebSocket connection opened
2021-01-22 08:31:21,594 ServerConnection created
2021-01-22 08:31:21,767 Failed sending message as connection was closed
The problem when running as an embedded session goes away if the number of variables in the data source is reduced with all other pieces are unchanged. The problem does not occur when accessing via the bokeh server directly, e.g. navigating to localhost:5006/bkapp
This is a trivial, minimal example that is uninteresting from an engineering standpoint. However, the motivation is to support a real-world application that performs multivariate time-series visualization and analysis. For brevity and ease of reproducing, I’ve stripped away all of the pieces related to uploading large-ish user data, callbacks, analysis functions and interactive plots.
The minimal reproducible example includes the following parts
bkapp.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
"""
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.io import curdoc
rows, cols = (10000, 100)
data = pd.DataFrame(data=np.random.randn(rows, cols), columns=['x'+str(i) for i in range(cols)])
source = ColumnDataSource(data=data)
p = figure(x_axis_type='linear', title="Flask-Bokeh Example")
p.scatter('x0', 'x1', source=source)
curdoc().add_root(p)
flask_bokeh.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Flask+bokeh app
"""
from flask import Flask, render_template
from bokeh.client import pull_session
from bokeh.embed import server_session
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
arguments = None
with pull_session(url='http://localhost:5006/bkapp', arguments=arguments) as _session:
script = server_session(session_id=_session.id, url='http://localhost:5006/bkapp')
return render_template("index.html", script=script, template="Flask")
if __name__ == '__main__':
app.run(port=8000)
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Embedding a Bokeh Server With {{ framework }}</title>
</head>
<body>
<div>
This Bokeh app below served by a Bokeh server that has been embedded
in another web app framework. For more information see the section
<a target="_blank" href="https://docs.bokeh.org/en/latest/docs/user_guide/server.html#embedding-bokeh-server-as-a-library">Embedding Bokeh Server as a Library</a>
in the User's Guide.
</div>
{{ script|safe }}
</body>
</html>