From 4eef31439e8782271051542a55b37de58a4a0e41 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Sep 2017 19:24:21 +0100 Subject: [PATCH 1/4] Added support for sending buffers to Comms --- holoviews/plotting/comms.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/holoviews/plotting/comms.py b/holoviews/plotting/comms.py index ebfcf2a52a..dcad88b699 100644 --- a/holoviews/plotting/comms.py +++ b/holoviews/plotting/comms.py @@ -67,7 +67,7 @@ def init(self, on_msg=None): """ - def send(self, data): + def send(self, data=None, buffers=[]): """ Sends data to the frontend """ @@ -165,13 +165,13 @@ def decode(cls, msg): return msg['content']['data'] - def send(self, data): + def send(self, data=None, buffers=[]): """ Pushes data across comm socket. """ if not self._comm: self.init() - self.comm.send(data) + self.comm.send(data, buffers=buffers) @@ -216,9 +216,9 @@ def _handle_open(self, comm, msg): self._comm.on_msg(self._handle_msg) - def send(self, data): + def send(self, data=None, buffers=[]): """ Pushes data across comm socket. """ - self.comm.send(data) + self.comm.send(data, buffers=buffers) From dacd5e49bd5da67688e43dd23d79f0d7326aee52 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Sep 2017 19:25:38 +0100 Subject: [PATCH 2/4] Add support for sending data with bokeh binary protocol --- holoviews/plotting/bokeh/plot.py | 17 ++++++++++--- holoviews/plotting/bokeh/renderer.py | 30 ++++++++++++++++------- holoviews/plotting/bokeh/util.py | 36 +++++++++++++++++++++------- 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index 8ba96e82f4..e823a14888 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -1,6 +1,7 @@ +import json from itertools import groupby -import numpy as np +import numpy as np import param from bokeh.models import (ColumnDataSource, Column, Row, Div) @@ -111,8 +112,18 @@ def push(self): return if self.comm is None: raise Exception('Renderer does not have a comm.') - diff = self.renderer.diff(self) - self.comm.send(diff) + + if bokeh_version > '0.12.9': + msg = self.renderer.diff(self, binary=True) + self.comm.send(msg.header_json) + self.comm.send(msg.metadata_json) + self.comm.send(msg.content_json) + for header, payload in msg.buffers: + self.comm.send(json.dumps(header)) + self.comm.send(buffers=[payload]) + else: + diff = self.renderer.diff(self) + self.comm.send(diff) def set_root(self, root): diff --git a/holoviews/plotting/bokeh/renderer.py b/holoviews/plotting/bokeh/renderer.py index aa2f6d86ef..5b4cea7ed7 100644 --- a/holoviews/plotting/bokeh/renderer.py +++ b/holoviews/plotting/bokeh/renderer.py @@ -9,8 +9,10 @@ from bokeh.application import Application from bokeh.document import Document from bokeh.embed import notebook_div -from bokeh.io import load_notebook, curdoc, show as bkshow +from bokeh.io import curdoc, show as bkshow +from bokeh.io.notebook import load_notebook from bokeh.models import Model +from bokeh.protocol import Protocol from bokeh.resources import CDN, INLINE from bokeh.server.server import Server @@ -204,6 +206,8 @@ def server_doc(self_or_cls, obj, doc=None): def figure_data(self, plot, fmt='html', doc=None, **kwargs): model = plot.state doc = Document() if doc is None else doc + if bokeh_version > '0.12.9': + doc.hold() for m in model.references(): m._document = None doc.add_root(model) @@ -222,15 +226,21 @@ def figure_data(self, plot, fmt='html', doc=None, **kwargs): return div - def diff(self, plot, serialize=True): + def diff(self, plot, serialize=True, binary=False): """ Returns a json diff required to update an existing plot with the latest plot data. """ - plotobjects = [h for handles in plot.traverse(lambda x: x.current_handles, [lambda x: x._updated]) - for h in handles] - plot.traverse(lambda x: setattr(x, '_updated', False)) - patch = compute_static_patch(plot.document, plotobjects) + if binary: + events = list(self.document._held_events) + msg = Protocol("1.0").create("PATCH-DOC", events) + self.document._held_events = [] + return msg + else: + plotobjects = [h for handles in plot.traverse(lambda x: x.current_handles, [lambda x: x._updated]) + for h in handles] + plot.traverse(lambda x: setattr(x, '_updated', False)) + patch = compute_static_patch(plot.document, plotobjects) processed = self._apply_post_render_hooks(patch, plot, 'json') return serialize_json(processed) if serialize else processed @@ -284,5 +294,9 @@ def load_nb(cls, inline=True): """ kwargs = {'notebook_type': 'jupyter'} if '0.12.9' >= bokeh_version > '0.12.5' else {} load_notebook(hide_banner=True, resources=INLINE if inline else CDN, **kwargs) - from bokeh.io import _state - _state.output_notebook() + if bokeh_version < '0.12.9': + from bokeh.io import _state + _state.output_notebook() + else: + from bokeh.io.notebook import curstate + curstate().output_notebook() diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index e480ca5eb4..1c2d89006e 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -31,9 +31,6 @@ except: Chart = type(None) # Create stub for isinstance check -if bokeh_version > '0.12.7': - from bokeh.document.util import _event_for_attribute_change - from ...core.options import abbreviated_exception from ...core.overlay import Overlay from ...core.util import basestring, unique_array, callable_name, pd, dt64_to_dt @@ -346,6 +343,32 @@ def to_references(doc): return references +def _value_record_references(all_references, value, result): + if value is None: return + if isinstance(value, dict) and set(['id', 'type']).issubset(set(value.keys())): + if value['id'] not in result: + ref = all_references[value['id']] + result[value['id']] = ref + _value_record_references(all_references, ref['attributes'], result) + elif isinstance(value, (list, tuple)): + for elem in value: + _value_record_references(all_references, elem, result) + elif isinstance(value, dict): + for k, elem in value.items(): + _value_record_references(all_references, elem, result) + + +def _event_for_attribute_change(all_references, changed_obj, key, new_value, value_refs): + event = dict( + kind='ModelChanged', + model=dict(id=changed_obj['id'], type=changed_obj['type']), + attr=key, + new=new_value, + ) + _value_record_references(all_references, new_value, value_refs) + return event + + def compute_static_patch(document, models): """ Computes a patch to update an existing document without @@ -385,12 +408,7 @@ def compute_static_patch(document, models): else: priority = float('inf') for key, val in obj['attributes'].items(): - if bokeh_version > '0.12.7': - event = _event_for_attribute_change(references, obj, key, val, value_refs) - else: - event = Document._event_for_attribute_change(references, - obj, key, val, - value_refs) + event = _event_for_attribute_change(references, obj, key, val, value_refs) events.append((priority, event)) update_types[obj['type']].append(key) events = [delete_refs(e, IGNORED_MODELS, ignored_attributes=IGNORED_ATTRIBUTES) From 61fa30fc540b09f9d94f79ec878015366710602b Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Sep 2017 20:37:59 +0100 Subject: [PATCH 3/4] Fixed small bug --- holoviews/plotting/bokeh/renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/holoviews/plotting/bokeh/renderer.py b/holoviews/plotting/bokeh/renderer.py index 5b4cea7ed7..a9d2c8ab80 100644 --- a/holoviews/plotting/bokeh/renderer.py +++ b/holoviews/plotting/bokeh/renderer.py @@ -232,9 +232,9 @@ def diff(self, plot, serialize=True, binary=False): the latest plot data. """ if binary: - events = list(self.document._held_events) + events = list(plot.document._held_events) msg = Protocol("1.0").create("PATCH-DOC", events) - self.document._held_events = [] + plot.document._held_events = [] return msg else: plotobjects = [h for handles in plot.traverse(lambda x: x.current_handles, [lambda x: x._updated]) From 2e6cfe8999b647621df7557800a45ebccbfab044 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Sep 2017 23:04:53 +0100 Subject: [PATCH 4/4] Wrap load_notebook import in version check --- holoviews/plotting/bokeh/renderer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/holoviews/plotting/bokeh/renderer.py b/holoviews/plotting/bokeh/renderer.py index a9d2c8ab80..d3e6fa8571 100644 --- a/holoviews/plotting/bokeh/renderer.py +++ b/holoviews/plotting/bokeh/renderer.py @@ -10,9 +10,7 @@ from bokeh.document import Document from bokeh.embed import notebook_div from bokeh.io import curdoc, show as bkshow -from bokeh.io.notebook import load_notebook from bokeh.models import Model -from bokeh.protocol import Protocol from bokeh.resources import CDN, INLINE from bokeh.server.server import Server @@ -24,6 +22,12 @@ from .util import (compute_static_patch, serialize_json, attach_periodic, bokeh_version, compute_plot_size) +if bokeh_version > '0.12.9': + from bokeh.io.notebook import load_notebook + from bokeh.protocol import Protocol +else: + from bokeh.io import load_notebook + class BokehRenderer(Renderer):