diff --git a/src/fastcs/attributes.py b/src/fastcs/attributes.py index 5e9b5d07..b99b6b8f 100644 --- a/src/fastcs/attributes.py +++ b/src/fastcs/attributes.py @@ -41,6 +41,19 @@ class Handler(Sender, Updater, Protocol): pass +class SimpleHandler(Handler): + """Handler for internal parameters""" + + async def put(self, controller: Any, attr: AttrW, value: Any): + await attr.update_display_without_process(value) + + if isinstance(attr, AttrRW): + await attr.set(value) + + async def update(self, controller: Any, attr: AttrR): + raise RuntimeError("SimpleHandler cannot update") + + class Attribute(Generic[T]): """Base FastCS attribute. @@ -171,7 +184,11 @@ def __init__( ) self._process_callback: AttrCallback[T] | None = None self._write_display_callback: AttrCallback[T] | None = None - self._sender = handler + + if handler is not None: + self._sender = handler + else: + self._sender = SimpleHandler() async def process(self, value: T) -> None: await self.process_without_display_update(value) @@ -195,7 +212,7 @@ def set_write_display_callback(self, callback: AttrCallback[T] | None) -> None: self._write_display_callback = callback @property - def sender(self) -> Sender | None: + def sender(self) -> Sender: return self._sender diff --git a/tests/test_attribute.py b/tests/test_attribute.py index eca1ffb8..c3cf8c4a 100644 --- a/tests/test_attribute.py +++ b/tests/test_attribute.py @@ -1,8 +1,9 @@ from functools import partial import pytest +from pytest_mock import MockerFixture -from fastcs.attributes import AttrR, AttrRW +from fastcs.attributes import AttrR, AttrRW, AttrW from fastcs.datatypes import Int, String @@ -31,3 +32,28 @@ async def device_add(): await attr_rw.process(2) assert device["number"] == 2 assert ui["number"] == 2 + + +@pytest.mark.asyncio +async def test_simple_handler_w(mocker: MockerFixture): + attr = AttrW(Int()) + update_display_mock = mocker.patch.object(attr, "update_display_without_process") + + # This is called by the transport when it receives a put + await attr.sender.put(mocker.ANY, attr, 1) + + # The callback to update the transport display should be called + update_display_mock.assert_called_once_with(1) + + +@pytest.mark.asyncio +async def test_simple_handler_rw(mocker: MockerFixture): + attr = AttrRW(Int()) + update_display_mock = mocker.patch.object(attr, "update_display_without_process") + set_mock = mocker.patch.object(attr, "set") + + await attr.sender.put(mocker.ANY, attr, 1) + + update_display_mock.assert_called_once_with(1) + # The Sender of the attribute should just set the value on the attribute + set_mock.assert_awaited_once_with(1)