From 44978bd51438fcf35275f5ccc309c1266780fc41 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 10 Apr 2019 12:48:39 +0100 Subject: [PATCH] Add support for passing in parameter instances as streams --- holoviews/streams.py | 41 +++++++++++++++++++++------------- holoviews/tests/teststreams.py | 38 ++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/holoviews/streams.py b/holoviews/streams.py index 0b439cc1d1..4543df8cb6 100644 --- a/holoviews/streams.py +++ b/holoviews/streams.py @@ -166,30 +166,41 @@ def trigger(cls, streams): def _on_trigger(self): """Called when a stream has been triggered""" + @classmethod def _process_streams(cls, streams): """ Processes a list of streams promoting Parameterized objects and methods to Param based streams. """ - param_watch_support = util.param_version >= '1.8.0' - parameterizeds = [s.parameterized for s in streams if isinstance(s, Params)] + parameterizeds = defaultdict(list) valid, invalid = [], [] for s in streams: - if not isinstance(s, Stream): - if isinstance(s, param.Parameterized) and param_watch_support: - if s not in parameterizeds: - s = Params(s) - else: - continue - elif util.is_param_method(s) and param_watch_support: - if not hasattr(s, "_dinfo") or util.get_method_owner(s) in parameterizeds: - continue - else: - s = ParamMethod(s) - else: - invalid.append(s) + if isinstance(s, Stream): + pass + elif isinstance(s, param.Parameter): + s = Params(s.owner, [s.name]) + elif isinstance(s, param.Parameterized): + s = Params(s) + elif util.is_param_method(s): + if not hasattr(s, "_dinfo"): continue + s = ParamMethod(s) + else: + invalid.append(s) + continue + if isinstance(s, Params): + pid = id(s.parameterized) + if pid in parameterizeds: + overlap = (set(s.parameters) & set(parameterizeds[pid])) + if overlap: + pname = type(s.parameterized).__name__ + raise ValueError('Found multiple Params streams ' + 'subscribing to the %s parameter(s) ' + 'on the %s object. Ensure that ' + 'you only subscribe to a parameter ' + 'once.' % (list(overlap), pname)) + parameterizeds[pid] += s.parameters valid.append(s) return valid, invalid diff --git a/holoviews/tests/teststreams.py b/holoviews/tests/teststreams.py index f0c2dd1869..dda36dc609 100644 --- a/holoviews/tests/teststreams.py +++ b/holoviews/tests/teststreams.py @@ -181,9 +181,6 @@ def test_class_value_update(self): class TestParamsStream(ComparisonTestCase): def setUp(self): - if LooseVersion(param.__version__) < '1.8.0': - raise SkipTest('Params stream requires param >= 1.8.0') - class Inner(param.Parameterized): x = param.Number(default = 0) @@ -223,6 +220,41 @@ def subscriber(**kwargs): inner.y = 2 self.assertEqual(values, [{'x': 2, 'y': 2}]) + def test_param_stream_instance_separate_parameters(self): + inner = self.inner() + + xparam = Params(inner, ['x']) + yparam = Params(inner, ['y']) + + valid, invalid = Stream._process_streams([xparam, yparam]) + self.assertEqual(len(valid), 2) + self.assertEqual(len(invalid), 0) + + def test_param_stream_instance_overlapping_parameters(self): + inner = self.inner() + + params1 = Params(inner) + params2 = Params(inner) + + with self.assertRaises(ValueError): + Stream._process_streams([params1, params2]) + + def test_param_parameter_instance_separate_parameters(self): + inner = self.inner() + + valid, invalid = Stream._process_streams([inner.param.x, inner.param.y]) + xparam, yparam = valid + + self.assertIs(xparam.parameterized, inner) + self.assertEqual(xparam.parameters, ['x']) + self.assertIs(yparam.parameterized, inner) + self.assertEqual(yparam.parameters, ['y']) + + def test_param_parameter_instance_overlapping_parameters(self): + inner = self.inner() + with self.assertRaises(ValueError): + Stream._process_streams([inner.param.x, inner.param.x]) + def test_param_stream_parameter_override(self): inner = self.inner(x=2) stream = Params(inner, parameters=['x'])