Hi everyone,
I am trying to use the streaming capabilities of Bokeh in combination with the Panel library. More specifically, I am trying to use ColumnDataSource.stream
(from Bokeh) in a callback function that should be periodically called. However, I do not manage to make this work: if I add the periodic callback to the bokeh.io.curdoc
, the callback is never called and if I add the periodic callback to any Panel object, then I get a Bokeh Error.
You will find below two minimal examples that do not work. Here are some informations about my runs:
- Panel version
0.9.7
- Bokeh version
2.1.1
- Python version
3.7.3
- OS: Linux
Using panel.add_periodic_callback
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
import numpy as np
import panel as pn
ds = ColumnDataSource({"x": [0], "y": [0]})
i = 1
def update(event=None):
global i
ds.stream({"x": [i], "y": [np.random.rand()]})
i += 1
p = figure()
p.line(x="x", y="y", source=ds)
pane = pn.panel(p)
pane.add_periodic_callback(update, 200)
In this case, the update function is called just fine, but as soon as I try to visualize the plot (i.e. opening the standalone server in a browser tab or showing the pane
object in a jupyter cell), then I get the following error from Bokeh:
ERROR:tornado.application:Exception in callback <bound method PeriodicCallback._periodic_callback of PeriodicCallback(callback=<function update at 0x7f428be6b1e0>, count=None, name='PeriodicCallback00006', period=200, timeout=None)>
Traceback (most recent call last):
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/tornado/ioloop.py", line 907, in _run
return self.callback()
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/panel/callbacks.py", line 65, in _periodic_callback
self.callback()
File "test.py", line 14, in update
ds.stream({"x": [i], "y": [np.random.rand()]})
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/models/sources.py", line 415, in stream
self._stream(new_data, rollover)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/models/sources.py", line 527, in _stream
self.data._stream(self.document, self, new_data, rollover, setter)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/core/property/wrappers.py", line 430, in _stream
hint=ColumnsStreamedEvent(doc, source, new_data, rollover, setter))
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/core/property/wrappers.py", line 150, in _notify_owners
descriptor._notify_mutated(owner, old, hint=hint)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/core/property/descriptors.py", line 869, in _notify_mutated
self._real_set(obj, old, value, hint=hint)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/core/property/descriptors.py", line 832, in _real_set
self._trigger(obj, old, value, hint=hint, setter=setter)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/core/property/descriptors.py", line 909, in _trigger
obj.trigger(self.name, old, value, hint, setter)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/model.py", line 661, in trigger
super().trigger(attr, old, new, hint=hint, setter=setter)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/util/callback_manager.py", line 157, in trigger
self._document._notify_change(self, attr, old, new, hint, setter, invoke)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/document.py", line 1042, in _notify_change
self._trigger_on_change(event)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/document.py", line 1137, in _trigger_on_change
self._with_self_as_curdoc(invoke_callbacks)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/document.py", line 1150, in _with_self_as_curdoc
return f()
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/document.py", line 1136, in invoke_callbacks
cb(event)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/document.py", line 704, in <lambda>
self._callbacks[receiver] = lambda event: event.dispatch(receiver)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/events.py", line 269, in dispatch
super().dispatch(receiver)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/document/events.py", line 124, in dispatch
receiver._document_patched(self)
File "/home/jokteur/anaconda3/lib/python3.7/site-packages/bokeh/server/session.py", line 218, in _document_patched
raise RuntimeError("_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes")
RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes
From what I understand, is that Panel added the periodic callback to the tornadio IOLoop, but Bokeh does not allow for ColumnDataSource.stream
to be called outside of a normal Bokeh document periodic callback.
Using curdoc().add_periodic_callback
I tried also to simply add the periodic callback to the curdoc()
, but here the problem is that update
never gets called, even when I have an active session that is opened:
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.io import curdoc
import numpy as np
import panel as pn
ds = ColumnDataSource({"x": [0], "y": [0]})
i = 1
def update(event=None):
global i
ds.stream({"x": [i], "y": [np.random.rand()]})
i += 1
p = figure()
p.line(x="x", y="y", source=ds)
pane = pn.panel(p)
curdoc().add_periodic_callback(update, 200)
Conclusion
I am aware that holoviews
possess a streaming module, but I would like to stick to Bokeh
and Panel
only if possible. In the worst case I can not make this periodic callback work, then I would probably abandon Panel
(in worked for me in an Bokeh
only app, even if there are some nice widgets that I could use.
Thank you in advance for all the help you can give.