From 735fe5e20e722a2a98fea0da37a71b23c79ebb52 Mon Sep 17 00:00:00 2001 From: Charles Mita Date: Tue, 21 Jun 2016 10:48:50 +0100 Subject: [PATCH] Add writeable property to Method Disallows __call__ if not set to true (true is the default value). --- malcolm/core/method.py | 11 ++++++++ .../test_controllers/test_clientcontroller.py | 1 + tests/test_core/test_method.py | 25 ++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/malcolm/core/method.py b/malcolm/core/method.py index c143a708d..fe0fc4396 100644 --- a/malcolm/core/method.py +++ b/malcolm/core/method.py @@ -16,6 +16,7 @@ def __init__(self, name, description): self.takes = None self.returns = None self.defaults = None + self.writeable = True def set_function(self, func): """Set the function to expose. @@ -41,11 +42,19 @@ def set_function_returns(self, return_meta): """Set the return parameters for the method to validate against""" self.returns = return_meta + def set_writeable(self, writeable): + """Set writeable property to enable or disable calling method""" + self.writeable = writeable + self.on_changed([[["writeable"], writeable]]) + def __call__(self, *args, **kwargs): """Call the exposed function using regular keyword argument parameters. Will validate the output against provided return parameters. """ + if not self.writeable: + raise ValueError("Can not call a method that is not writeable") + # Assumes positional arguments represent arguments *before* any kw-args # in the ordered dictionary. for arg, arg_val in zip(self.takes.elements.keys(), args): @@ -95,6 +104,7 @@ def to_dict(self): serialized["takes"] = self.takes.to_dict() serialized["defaults"] = self.defaults.copy() serialized["returns"] = self.returns.to_dict() + serialized["writeable"] = self.writeable return serialized @classmethod @@ -110,6 +120,7 @@ def from_dict(cls, name, d): method.set_function_takes(takes, d["defaults"]) returns = MapMeta.from_dict("returns", d["returns"]) method.set_function_returns(returns) + method.writeable = d["writeable"] return method @classmethod diff --git a/tests/test_controllers/test_clientcontroller.py b/tests/test_controllers/test_clientcontroller.py index cd85586cb..2c3caee93 100644 --- a/tests/test_controllers/test_clientcontroller.py +++ b/tests/test_controllers/test_clientcontroller.py @@ -45,6 +45,7 @@ def setUp(self): ), required=["response"], ), + writeable=True, ), ) diff --git a/tests/test_core/test_method.py b/tests/test_core/test_method.py index 2f2ffcc31..fdb371a12 100644 --- a/tests/test_core/test_method.py +++ b/tests/test_core/test_method.py @@ -149,6 +149,23 @@ def test_handle_request_error(self): self.assertEquals( "Method test_method raised an error: Test error", response.message) + def test_not_writeable_stops_call(self): + m = Method("test_method", "test_description") + m.set_function(Mock()) + m.set_writeable(False) + with self.assertRaises(ValueError, + msg="Can not call a method that is not writeable"): + m() + + def test_set_writeable_notifies(self): + m = Method("test_method", "test_description") + m.on_changed = MagicMock(side_effect=m.on_changed) + m.set_writeable(False) + m.on_changed.assert_called_once_with([[["writeable"], False]]) + m.on_changed.reset_mock() + m.set_writeable(True) + m.on_changed.assert_called_once_with([[["writeable"], True]]) + def test_to_dict_serialization(self): func = Mock(return_value={"out": "dummy"}) defaults = {"in_attr": "default"} @@ -158,15 +175,18 @@ def test_to_dict_serialization(self): return_meta = Mock(elements={"out": Mock()}, to_dict=Mock( return_value=OrderedDict({"dict": "return"}))) + writeable_mock = Mock() m = Method("test_method", "test_description") m.set_function(func) m.set_function_takes(args_meta, defaults) m.set_function_returns(return_meta) + m.set_writeable(writeable_mock) expected = OrderedDict() expected["description"] = "test_description" expected["takes"] = OrderedDict({"dict": "args"}) expected["defaults"] = OrderedDict({"in_attr": "default"}) expected["returns"] = OrderedDict({"dict": "return"}) + expected["writeable"] = writeable_mock self.assertEquals(expected, m.to_dict()) @patch("malcolm.core.method.MapMeta") @@ -176,8 +196,10 @@ def test_from_dict_deserialize(self, mock_mapmeta): takes = dict(a=object(), b=object()) returns = dict(c=object()) defaults = dict(a=43) + writeable_mock = Mock() d = dict(description=description, takes=takes, - returns=returns, defaults=defaults) + returns=returns, defaults=defaults, + writeable=writeable_mock) m = Method.from_dict(name, d) self.assertEqual(mock_mapmeta.from_dict.call_args_list, [ call("takes", takes), call("returns", returns)]) @@ -185,6 +207,7 @@ def test_from_dict_deserialize(self, mock_mapmeta): self.assertEqual(m.takes, mock_mapmeta.from_dict.return_value) self.assertEqual(m.returns, mock_mapmeta.from_dict.return_value) self.assertEqual(m.defaults, defaults) + self.assertEquals(m.writeable, writeable_mock) @patch("malcolm.core.method.MapMeta") def test_takes_given_optional(self, map_meta_mock):