Skip to content

Commit

Permalink
Support for using bokeh protocol in the notebook including binary tra…
Browse files Browse the repository at this point in the history
…nsfer (#1894)
  • Loading branch information
philippjfr authored and jlstevens committed Sep 19, 2017
1 parent c1858f1 commit f8bb486
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 25 deletions.
17 changes: 14 additions & 3 deletions holoviews/plotting/bokeh/plot.py
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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):
Expand Down
34 changes: 26 additions & 8 deletions holoviews/plotting/bokeh/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
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.models import Model
from bokeh.resources import CDN, INLINE
from bokeh.server.server import Server
Expand All @@ -22,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):

Expand Down Expand Up @@ -204,6 +210,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)
Expand All @@ -222,15 +230,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(plot.document._held_events)
msg = Protocol("1.0").create("PATCH-DOC", events)
plot.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

Expand Down Expand Up @@ -284,5 +298,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()
36 changes: 27 additions & 9 deletions holoviews/plotting/bokeh/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 5 additions & 5 deletions holoviews/plotting/comms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand Down Expand Up @@ -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)



Expand Down Expand Up @@ -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)

0 comments on commit f8bb486

Please sign in to comment.