Skip to content

Commit

Permalink
Ensure that bokeh plot refresh has the Document lock (#3621)
Browse files Browse the repository at this point in the history
* Ensure that bokeh plot refresh has the Document lock

* Ensure curdoc is set during tests
  • Loading branch information
philippjfr authored Apr 26, 2019
1 parent 0f4563f commit a1213a5
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 0 deletions.
7 changes: 7 additions & 0 deletions holoviews/plotting/bokeh/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import numpy as np
import param

from bokeh.io import curdoc
from bokeh.layouts import gridplot
from bokeh.models import (ColumnDataSource, Column, Row, Div)
from bokeh.models.widgets import Panel, Tabs
Expand Down Expand Up @@ -175,6 +176,12 @@ def _construct_callbacks(self):
cbs.append(cb(self, cb_streams, source))
return cbs

def refresh(self, **kwargs):
if self.renderer.mode == 'server' and curdoc() is not self.document:
# If we do not have the Document lock, schedule refresh as callback
self.document.add_next_tick_callback(self.refresh)
else:
super(BokehPlot, self).refresh(**kwargs)

def push(self):
"""
Expand Down
8 changes: 8 additions & 0 deletions holoviews/tests/plotting/bokeh/testcallbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

try:
from bokeh.events import Tap
from bokeh.io.doc import set_curdoc
from bokeh.models import Range1d, Plot, ColumnDataSource, Selection, PolyEditTool
from holoviews.plotting.bokeh.callbacks import (
Callback, PointDrawCallback, PolyDrawCallback, PolyEditCallback,
Expand Down Expand Up @@ -49,6 +50,7 @@ def test_stream_callback(self):
dmap = DynamicMap(lambda x, y: Points([(x, y)]), kdims=[], streams=[PointerXY()])
plot = bokeh_server_renderer.get_plot(dmap)
bokeh_server_renderer(plot)
set_curdoc(plot.document)
plot.callbacks[0].on_msg({"x": 0.3, "y": 0.2})
data = plot.handles['source'].data
self.assertEqual(data['x'], np.array([0.3]))
Expand All @@ -58,6 +60,7 @@ def test_point_stream_callback_clip(self):
dmap = DynamicMap(lambda x, y: Points([(x, y)]), kdims=[], streams=[PointerXY()])
plot = bokeh_server_renderer.get_plot(dmap)
bokeh_server_renderer(plot)
set_curdoc(plot.document)
plot.callbacks[0].on_msg({"x": -0.3, "y": 1.2})
data = plot.handles['source'].data
self.assertEqual(data['x'], np.array([0]))
Expand All @@ -68,6 +71,7 @@ def test_stream_callback_on_clone(self):
stream = PointerXY(source=points)
plot = bokeh_server_renderer.get_plot(points.clone())
bokeh_server_renderer(plot)
set_curdoc(plot.document)
plot.callbacks[0].on_msg({"x": 0.8, "y": 0.3})
self.assertEqual(stream.x, 0.8)
self.assertEqual(stream.y, 0.3)
Expand All @@ -83,6 +87,7 @@ def test_stream_callback_with_ids(self):
dmap = DynamicMap(lambda x, y: Points([(x, y)]), kdims=[], streams=[PointerXY()])
plot = bokeh_server_renderer.get_plot(dmap)
bokeh_server_renderer(plot)
set_curdoc(plot.document)
model = plot.state
plot.callbacks[0].on_msg({"x": {'id': model.ref['id'], 'value': 0.5},
"y": {'id': model.ref['id'], 'value': 0.4}})
Expand All @@ -98,6 +103,7 @@ def history_callback(x, history=deque(maxlen=10)):
dmap = DynamicMap(history_callback, kdims=[], streams=[stream])
plot = bokeh_server_renderer.get_plot(dmap)
bokeh_server_renderer(plot)
set_curdoc(plot.document)
for i in range(20):
stream.event(x=i)
data = plot.handles['source'].data
Expand Down Expand Up @@ -137,6 +143,7 @@ def test_pointer_x_datetime_out_of_bounds(self):
points = Points([(dt.datetime(2017, 1, 1), 1), (dt.datetime(2017, 1, 3), 3)])
PointerX(source=points)
plot = bokeh_server_renderer.get_plot(points)
set_curdoc(plot.document)
callback = plot.callbacks[0]
self.assertIsInstance(callback, PointerXCallback)
msg = callback._process_msg({'x': 1000})
Expand All @@ -148,6 +155,7 @@ def test_tap_datetime_out_of_bounds(self):
points = Points([(dt.datetime(2017, 1, 1), 1), (dt.datetime(2017, 1, 3), 3)])
SingleTap(source=points)
plot = bokeh_server_renderer.get_plot(points)
set_curdoc(plot.document)
callback = plot.callbacks[0]
self.assertIsInstance(callback, TapCallback)
msg = callback._process_msg({'x': 1000, 'y': 2})
Expand Down

0 comments on commit a1213a5

Please sign in to comment.