Skip to content

Commit

Permalink
Improved document and server handling
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Aug 15, 2019
1 parent 274a6b2 commit 34bd140
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 22 deletions.
8 changes: 7 additions & 1 deletion holoviews/plotting/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
"""
from __future__ import absolute_import

import threading
import warnings

from itertools import groupby, product
from collections import Counter, defaultdict

import numpy as np
import param

from panel.io.notebook import push
from panel.io.state import state

from ..core import OrderedDict
from ..core import util, traversal
Expand Down Expand Up @@ -129,7 +132,10 @@ def refresh(self, **kwargs):
"""
if self.renderer.mode == 'server':
from bokeh.io import curdoc
if curdoc() is not self.document:
thread = threading.current_thread()
thread_id = thread.ident if thread else None
if (curdoc() is not self.document or (state._thread_id is not None and
thread_id == state._thread_id)):
# If we do not have the Document lock, schedule refresh as callback
self.document.add_next_tick_callback(self.refresh)
return
Expand Down
61 changes: 40 additions & 21 deletions holoviews/tests/plotting/bokeh/testserver.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import time
import threading

from unittest import SkipTest
from threading import Event
Expand All @@ -25,6 +26,7 @@
from holoviews.plotting.bokeh.renderer import BokehRenderer
from panel.widgets import DiscreteSlider, FloatSlider
from panel.io.server import StoppableThread
from panel.io.state import state
bokeh_renderer = BokehRenderer.instance(mode='server')
except:
bokeh_renderer = None
Expand All @@ -45,6 +47,8 @@ def tearDown(self):
bokeh_renderer.last_plot = None
Callback._callbacks = {}
Renderer.notebook_context = self.nbcontext
state.curdoc = None
curdoc().clear()

def test_render_server_doc_element(self):
obj = Curve([])
Expand Down Expand Up @@ -98,6 +102,7 @@ def setUp(self):
self._loaded = Event()
self._port = None
self._thread = None
self._server = None

def tearDown(self):
Store.current_backend = self.previous_backend
Expand All @@ -107,6 +112,13 @@ def tearDown(self):
self._thread.stop()
except:
pass
state._thread_id = None
if self._server is not None:
try:
self._server.stop()
except:
pass
time.sleep(1)

def _launcher(self, obj, threaded=False, io_loop=None):
if io_loop:
Expand All @@ -120,8 +132,11 @@ def modify_doc(doc):
server = Server({'/': app}, port=0, io_loop=io_loop)
server.start()
self._port = server.port
self._server = server
if threaded:
server.io_loop.add_callback(self._loaded.set)
thread = threading.current_thread()
state._thread_id = thread.ident if thread else None
io_loop.start()
else:
url = "http://localhost:" + str(server.port) + "/"
Expand All @@ -148,8 +163,7 @@ def session(self):

def test_launch_simple_server(self):
obj = Curve([])
_, server = self._launcher(obj)
server.stop()
self._launcher(obj)

def test_launch_server_with_stream(self):
obj = Curve([])
Expand Down Expand Up @@ -185,27 +199,30 @@ def test_server_dynamicmap_with_dims(self):

cds = session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 0.1)
def set_slider():
slider = obj.layout.select(FloatSlider)[0]
slider = obj.layout.select(FloatSlider)[0]
def run():
slider.value = 3.1
doc.add_next_tick_callback(set_slider)
time.sleep(0.5)

cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 3.1)
doc.add_next_tick_callback(run)

def test():
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 3.1)
doc.add_next_tick_callback(test)

def test_render_dynamicmap_with_stream(self):
stream = Stream.define('Custom', y=2)()
dmap = DynamicMap(lambda y: Curve([1, 2, y]), kdims=['y'], streams=[stream])
obj, _ = bokeh_renderer._validate(dmap, None)
session = self._threaded_launcher(obj)
[(doc, _)] = obj.layout._documents.items()

cds = session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 2)
stream.event(y=3)
time.sleep(0.5)
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 3)
def test():
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 3)
doc.add_next_tick_callback(test)

def test_render_dynamicmap_with_stream_dims(self):
stream = Stream.define('Custom', y=2)()
Expand All @@ -218,15 +235,17 @@ def test_render_dynamicmap_with_stream_dims(self):
orig_cds = session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(orig_cds.data['y'][2], 2)
stream.event(y=3)
time.sleep(0.5)
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 3)
def test1():
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][2], 3)
doc.add_next_tick_callback(test1)

self.assertEqual(orig_cds.data['y'][0], 1)
def set_slider():
slider = obj.layout.select(DiscreteSlider)[0]
slider = obj.layout.select(DiscreteSlider)[0]
def run():
slider.value = 3
doc.add_next_tick_callback(set_slider)
time.sleep(0.5)
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][0], 3)
doc.add_next_tick_callback(run)
def test2():
cds = self.session.document.roots[0].select_one({'type': ColumnDataSource})
self.assertEqual(cds.data['y'][0], 3)
doc.add_next_tick_callback(test2)

0 comments on commit 34bd140

Please sign in to comment.