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

Bokeh server support #959

Merged
merged 30 commits into from Apr 7, 2017
Merged

Bokeh server support #959

merged 30 commits into from Apr 7, 2017

Conversation

philippjfr
Copy link
Member

@philippjfr philippjfr commented Oct 31, 2016

Adds support for deploying any holoviews object as a bokeh server app.

  • BokehRenderer initializes server document
  • Initial support for handling stream events

To do:

  • Bokeh widget implementation to support DynamicMaps and HoloMaps with standard streams
  • Ensure that bokeh stream callbacks relying on cb_data and cb_obj work correctly.
  • Add example apps.

Optional/Future:

  • Provide easy way to tie a stream to any bokeh widget.
  • Support bokeh widgets in a notebook context.

Example app:

import numpy as np
import holoviews as hv
import holoviews.plotting.bokeh
from holoviews.operation.datashader import datashade

hv.Store.current_backend = 'bokeh'
renderer = hv.Store.renderers['bokeh'].instance(mode='server', holomap='server')
points = hv.HoloMap({i: hv.Points(np.random.multivariate_normal((0,0), [[0.1, 0.1], [0.1, 1.0]], (1000000,))) for i in range(10)})
datashaded = datashade(points, x_sampling=0.01, y_sampling=0.01)
doc, _ = renderer(datashaded)
doc.title = 'HoloViews Datashade'

hv_datashade_server

@jlstevens
Copy link
Contributor

Nice to have the examples!

That said, I feel they belong in contrib which is an entire repo dedicated to various examples. I would put them in the scripts directory here.

@philippjfr
Copy link
Member Author

philippjfr commented Feb 3, 2017

I do like the idea of contrib, but until it's been integrated into our doc building process and referenced throughout our documentation it just seems like no one will ever find it.

Edit: I do think it's the right place for these examples.

@jlstevens
Copy link
Contributor

Given that we want to split out the bokeh backend eventually and this is definitely bokeh related, it should be in plotting.bokeh.examples if you don't want it in contrib.

@philippjfr
Copy link
Member Author

it should be in plotting.bokeh.examples if you don't want it in contrib.

I did think contrib was the right place, but plotting.bokeh.examples might actually be even better if we are going to split it out.

@jlstevens
Copy link
Contributor

I think it would be nice to keep all the examples together in contrib as it would show the breadth of what you can do with HoloViews. There is no restriction in contrib on backends or any other libraries necessary to make the examples work.

For this reason, I think we should move these examples to contrib right now - the bokeh backend repo could always link to the contrib examples to ensure people can discover them.

@philippjfr
Copy link
Member Author

philippjfr commented Mar 26, 2017

@jlstevens, @jbednar I've rebased this PR with the latest events PR and it's working as I want it to. The question is now about scope, I'll happily move the current examples to contrib, but my main question is about bokeh widgets. As of right now this PR ships with a small class that emulates our regular Dimension based widgets, and you can easily connect streams to other widgets and use the regular bokeh API to compose your new set of widgets into a layout with the plot(s).

Should we ship a basic widget class which generates widgets for HoloMap/DynamicMap key dimensions? You can see what that looks like in the gif example above. The implementation is fairly straightforward (~130 LOC) and means you can directly deploy standard HoloMaps/DynamicMaps you used and defined in a notebook without manually having to hook up widgets for key dimensions.

@jbednar
Copy link
Member

jbednar commented Mar 26, 2017

That all sounds good, but is there a reason not to just use Bokeh widgets all the time when we are using the Bokeh backend, even in the notebook? That seems like the way to have seamless interoperability with bokeh in its various possible usages.

@philippjfr
Copy link
Member Author

If we had already done the widget refactor and implemented a general widget/comm manager as we are planning to do I'd say yes. However for the time being I'd have to come up with some ad hoc solution which I'm strongly against. So my answer is that once we have that we can easily retrofit this widget implementation to support it.

In the long run we should find an easy and general way to map both dimensions and streams to specific widgets.

@philippjfr
Copy link
Member Author

Waiting on bokeh/bokeh#6062 so that the cb_obj can be handled the same for the js_on_event and on_event cases.

I've also implemented an event queue on the python end which works fairly well. Once the PR has been merged I'll have to extend that implementation to handle different event types as I did on the JS end.

@philippjfr
Copy link
Member Author

@jlstevens This is now ready for review. All streams now work for both bokeh server and in the notebook. I'm happy to move the example apps elsewhere though.

if self.plot.renderer.mode == 'default':
self.attach_callbacks()
self.state = self.init_layout()
self._event_queue = []
Copy link
Contributor

@jlstevens jlstevens Apr 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still rename this from _event_queue to just queue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is for the widgets now.. so it might be ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although maybe better for consistency? I'll rename.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in widgets now so my original comment was addressed. Might be worth renaming here too - up to you.

def _init_plot_handles(self):
"""
Find all requested plotting handles and cache them along
with the IDs of the models callbacks will be attached to.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either an an extra word here or words missing. Either '... along with the IDs the model callbacks will be attached to' or '...along with the IDs of the model callbacks that they will be attached to'.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither, it's missing a the:

along with the IDs of the models the callbacks will be attached to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works too. I knew something was wrong!

def process_on_change(self):
if not self._queue:
return
self._queue = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the _queue isn't used for process_on_change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because process_on_change just looks up the model values directly, that avoids having separate callbacks for each model change, which would be required otherwise.

the data sent by the callback before it is passed to the streams.
"""

def initialize(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better! :-)


mode = param.ObjectSelector(default='default',
objects=['default', 'server'], doc="""
Whether to render the DynamicMap in regular or server mode. """)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it only rendering DynamicMaps? Might want a bit more to say what server mode is about ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure why I wrote that. It handles anything.



@classmethod
def create_widget(self, dim, holomap=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much nicer as a classmethod! Good for testing...

@jlstevens
Copy link
Contributor

Fantastic!

Other than one docstring fix I am now very happy with this PR. The functionality is great and the code is now structured in a much more sensible way.

I'll merge once that is done.

@jlstevens
Copy link
Contributor

All done! Merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants