Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for using bokeh protocol in the notebook including binary transfer #1894

Merged
merged 4 commits into from
Sep 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)