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):