diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 7a2af712fc..d0b9025f0e 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -1,7 +1,7 @@ from collections import defaultdict import param -from bokeh.models import (CustomJS, FactorRange, DatetimeAxis, ColumnDataSource) +from bokeh.models import (CustomJS, FactorRange, DatetimeAxis, ColumnDataSource, Selection) from ...core import OrderedDict from ...streams import (Stream, PointerXY, RangeXY, Selection1D, RangeX, @@ -355,6 +355,9 @@ def resolve_attr_spec(cls, spec, cb_obj, model=None): continue if isinstance(resolved, dict): resolved = resolved.get(p) + elif isinstance(resolved, Selection) and p in ['1d', 'indices']: + # Handle resolving bokeh Selection 1d indices + resolved = resolved[p] else: resolved = getattr(resolved, p, None) return {'id': model.ref['id'], 'value': resolved} diff --git a/tests/testbokehcallbacks.py b/tests/testbokehcallbacks.py index 84426eb998..201935d3a4 100644 --- a/tests/testbokehcallbacks.py +++ b/tests/testbokehcallbacks.py @@ -12,7 +12,7 @@ from holoviews.plotting.bokeh.util import bokeh_version from bokeh.events import Tap - from bokeh.models import Range1d, Plot + from bokeh.models import Range1d, Plot, ColumnDataSource, Selection bokeh_renderer = Store.renderers['bokeh'] except: bokeh_renderer = None @@ -81,6 +81,12 @@ def test_server_callback_resolve_attr_spec_range1d_end(self): msg = Callback.resolve_attr_spec('x_range.attributes.end', range1d) self.assertEqual(msg, {'id': range1d.ref['id'], 'value': 10}) + def test_server_callback_resolve_attr_spec_source_selected(self): + source = ColumnDataSource() + source.selected = Selection(indices=[1, 2, 3]) + msg = Callback.resolve_attr_spec('cb_obj.selected.1d.indices', source) + self.assertEqual(msg, {'id': source.ref['id'], 'value': [1, 2, 3]}) + def test_server_callback_resolve_attr_spec_tap_event(self): plot = Plot() event = Tap(plot, x=42)