From 88f9878d0c2c9df83bd77e7c4ad152f9c80c2339 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 15 Aug 2020 10:31:01 -0400 Subject: [PATCH] Add History stream and tests --- holoviews/streams.py | 35 ++++++++++++++++++ holoviews/tests/teststreams.py | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/holoviews/streams.py b/holoviews/streams.py index 3419c3c06a..d612165470 100644 --- a/holoviews/streams.py +++ b/holoviews/streams.py @@ -855,6 +855,41 @@ def __del__(self): self._unregister_input_streams() +class History(Stream): + """ + A Stream that maintains a history of the values of a single input stream + """ + values = param.List(constant=True, doc=""" + List containing the historical values of the input stream""") + + def __init__(self, input_stream, **params): + super(History, self).__init__(**params) + self.input_stream = input_stream + self._register_input_stream() + # Trigger event on input stream after registering so that current value is + # added to our values list + self.input_stream.event() + + def clear_history(self): + self.values.clear() + self.event() + + def _register_input_stream(self): + """ + Register callback on input_stream to watch for changes + """ + def perform_update(**kwargs): + self.values.append(kwargs) + self.event() + + self.input_stream.add_subscriber(perform_update) + + def __del__(self): + self.input_stream.source = None + self.input_stream.clear() + self.values.clear() + + class SelectionExpr(Derived): selection_expr = param.Parameter(default=None, constant=True) diff --git a/holoviews/tests/teststreams.py b/holoviews/tests/teststreams.py index 9295efddcf..97a2b7201f 100644 --- a/holoviews/tests/teststreams.py +++ b/holoviews/tests/teststreams.py @@ -974,6 +974,72 @@ def test_exclusive_derived_stream(self): self.assertEqual(s0.v, -8.0) +class TestHistoryStream(ComparisonTestCase): + def test_initial_history_stream_values(self): + # Check values list is initialized with initial contents of input stream + val = Val(v=1.0) + history = History(val) + self.assertEqual(history.contents, {"values": [val.contents]}) + + def test_history_stream_values_appended(self): + val = Val(v=1.0) + history = History(val) + # Perform a few updates on val stream + val.event(v=2.0) + val.event(v=3.0) + self.assertEqual( + history.contents, + {"values": [{"v": 1.0}, {"v": 2.0}, {"v": 3.0}]} + ) + + # clear + history.clear_history() + self.assertEqual(history.contents, {"values": []}) + + def test_history_stream_trigger_callbacks(self): + # Construct history stream + val = Val(v=1.0) + history = History(val) + + # Register callback + callback_input = [] + def cb(**kwargs): + callback_input.append(kwargs) + history.add_subscriber(cb) + self.assertEqual(callback_input, []) + + # Perform updates on val stream and make sure history callback is triggered + callback_input.clear() + val.event(v=2.0) + self.assertEqual( + callback_input[0], + {"values": [{"v": 1.0}, {"v": 2.0}]} + ) + + callback_input.clear() + val.event(v=3.0) + self.assertEqual( + callback_input[0], + {"values": [{"v": 1.0}, {"v": 2.0}, {"v": 3.0}]} + ) + + # clearing history should trigger callback + callback_input.clear() + history.clear_history() + self.assertEqual( + callback_input[0], + {"values": []} + ) + + # Update after clearing + callback_input.clear() + val.event(v=4.0) + self.assertEqual( + callback_input[0], + {"values": [{"v": 4.0}]} + ) + + class TestExprSelectionStream(ComparisonTestCase): def setUp(self):