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

Param values stream #844

Merged
merged 2 commits into from
Sep 1, 2016
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
57 changes: 57 additions & 0 deletions holoviews/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,60 @@ class PositionXY(Stream):
def __init__(self, preprocessors=[], source=None, subscribers=[], **params):
super(PositionXY, self).__init__(preprocessors=preprocessors, source=source,
subscribers=subscribers, **params)




class ParamValues(Stream):
"""
A Stream based on the parameter values of some other parameterized
object, whether it is a parameterized class or a parameterized
instance.

The update method enables the stream to update the parameters of the
specified object.
"""
Copy link
Member

Choose a reason for hiding this comment

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

Can the docstring be improved? The PR makes it sound like the stream is updating the parameters, primarily, but what I thought was that it went the other way around, and here it seems to do both. Are these two very different usages? Seems like something to explain.

Copy link
Contributor Author

@jlstevens jlstevens Sep 2, 2016

Choose a reason for hiding this comment

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

Sure it can be improved but I don't think anything surprising is going on. Reading values from parameters is how streams specify their value and the update method is how they update those values. In other words this is exactly the behavior you would expect given how streams work in general (i.e the requirements specified by their API).

The only difference here that these parameters live on some other class/object instead of on the stream itself.

Copy link
Member

Choose a reason for hiding this comment

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

Well, in the case of paramnb the widgets set the parameter values directly and the update method won't really do anything, so there's two ways the parameters can be set. HoloViews doesn't care which one is actually used though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. Though if you aren't using update to actually update parameters, you can use Stream.trigger([callback]) instead. For paramnb it would be more awkward than update as you would need to wrap that in a lambda or something.

Copy link
Member

Choose a reason for hiding this comment

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

I'm still confused; seems like Philipp is saying that for paramnb update doesn't do anything, and Jean-Luc is saying that if not using update you'd have to do something else? I guess Philipp is saying that update doesn't do anything but triggering, with paramnb?

What seems like would help is if the docstring outlined at least two different expected usages for this object, one where the object updates the parameter values itself, which is what triggers changes elsewhere, and presumably one where it needs to consume changes to the parameters made elsewhere. Anyway, there's definitely something I'm confused about, but I may be confused about precisely what that is, so this may not be the real thing that needs explaining.


def __init__(self, obj, **params):
self._obj = obj
super(ParamValues, self).__init__(**params)


@property
def value(self):
if isinstance(self._obj, type):
remapped={k: getattr(self._obj,k)
for k in self._obj.params().keys() if k!= 'name'}
else:
remapped={k:v for k,v in self._obj.get_param_values() if k!= 'name'}

for preprocessor in self.preprocessors:
remapped = preprocessor(remapped)
return remapped


def update(self, trigger=True, **kwargs):
"""
The update method updates the parameters of the specified object.

If trigger is enabled, the trigger classmethod is invoked on
this Stream instance to execute its subscribers.
"""
if isinstance(self._obj, type):
for name in self._obj.params().keys():
if name in kwargs:
setattr(self._obj, name, kwargs[name])
else:
self._obj.set_param(**kwargs)

if trigger:
self.trigger([self])


def __repr__(self):
cls_name = self.__class__.__name__
return '%s(%r)' % (cls_name, self._obj)


def __str__(self):
return repr(self)
41 changes: 40 additions & 1 deletion tests/teststreams.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""
Unit test of the streams system
"""
import param
from holoviews.element.comparison import ComparisonTestCase
from holoviews.streams import Stream, PositionX, PositionY, PositionXY
from holoviews.streams import Stream, PositionX, PositionY, PositionXY, ParamValues
from holoviews.streams import Rename, Group


Expand Down Expand Up @@ -40,6 +41,44 @@ def test_positionY_const_parameter(self):
self.assertEqual(str(e), "Constant parameter 'y' cannot be modified")


class TestParamValuesStream(ComparisonTestCase):

def setUp(self):

class Inner(param.Parameterized):

x = param.Number(default = 0)
y = param.Number(default = 0)

self.inner = Inner

def tearDown(self):
self.inner.x = 0
self.inner.y = 0

def test_object_value(self):
obj = self.inner()
stream = ParamValues(obj)
self.assertEqual(stream.value, {'x':0, 'y':0})

def test_class_value(self):
stream = ParamValues(self.inner)
self.assertEqual(stream.value, {'x':0, 'y':0})

def test_object_value_update(self):
obj = self.inner()
stream = ParamValues(obj)
self.assertEqual(stream.value, {'x':0, 'y':0})
stream.update(x=5, y=10)
self.assertEqual(stream.value, {'x':5, 'y':10})

def test_class_value_update(self):
stream = ParamValues(self.inner)
self.assertEqual(stream.value, {'x':0, 'y':0})
stream.update(x=5, y=10)
self.assertEqual(stream.value, {'x':5, 'y':10})



class TestSubscribers(ComparisonTestCase):

Expand Down