From 84d0219a0532b0440ab1608ceee0c2a06d6418e0 Mon Sep 17 00:00:00 2001 From: Shihab Suliman Date: Mon, 11 Aug 2025 13:33:53 +0000 Subject: [PATCH 1/5] refactor: remove simplehandler from attrRW add to attrW --- src/fastcs/attributes.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/fastcs/attributes.py b/src/fastcs/attributes.py index ab0494e89..63fb56a17 100644 --- a/src/fastcs/attributes.py +++ b/src/fastcs/attributes.py @@ -49,12 +49,10 @@ class AttrHandlerRW(AttrHandlerR, AttrHandlerW): class SimpleAttrHandler(AttrHandlerRW): """Handler for internal parameters""" - async def put(self, attr: AttrW[T], value: T) -> None: + async def put(self, attr: AttrRW[T], value: T) -> None: # type: ignore + await attr.set(value) await attr.update_display_without_process(value) - if isinstance(attr, AttrRW): - await attr.set(value) - async def update(self, attr: AttrR) -> None: raise RuntimeError("SimpleHandler cannot update") @@ -187,11 +185,7 @@ def __init__( ) self._process_callbacks: list[AttrCallback[T]] | None = None self._write_display_callbacks: list[AttrCallback[T]] | None = None - - if handler is not None: - self._setter = handler - else: - self._setter = SimpleAttrHandler() + self._setter = handler async def process(self, value: T) -> None: await self.process_without_display_update(value) @@ -221,7 +215,7 @@ def add_write_display_callback(self, callback: AttrCallback[T]) -> None: self._write_display_callbacks.append(callback) @property - def sender(self) -> AttrHandlerW: + def sender(self) -> AttrHandlerW | None: return self._setter @@ -241,7 +235,7 @@ def __init__( datatype, # type: ignore access_mode, group=group, - handler=handler, + handler=handler if handler else SimpleAttrHandler(), initial_value=initial_value, description=description, ) From 911507d664501befd733c40cdbadd93c7049a6d3 Mon Sep 17 00:00:00 2001 From: Shihab Suliman Date: Mon, 11 Aug 2025 13:34:37 +0000 Subject: [PATCH 2/5] tests: amend simplehandler test with attrRW remove with attrW --- tests/test_attribute.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/test_attribute.py b/tests/test_attribute.py index 473501a75..39d5bec77 100644 --- a/tests/test_attribute.py +++ b/tests/test_attribute.py @@ -3,7 +3,14 @@ import pytest from pytest_mock import MockerFixture -from fastcs.attributes import AttrHandlerR, AttrHandlerRW, AttrR, AttrRW, AttrW +from fastcs.attributes import ( + AttrHandlerR, + AttrHandlerRW, + AttrHandlerW, + AttrR, + AttrRW, + AttrW, +) from fastcs.datatypes import Int, String @@ -34,29 +41,23 @@ async def device_add(): 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(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") + attr.update_display_without_process = mocker.MagicMock( + wraps=attr.update_display_without_process + ) + attr.set = mocker.MagicMock(wraps=attr.set) + + assert attr.sender + # This is called by the transport when it receives a put await attr.sender.put(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) + attr.update_display_without_process.assert_called_once_with(1) + attr.set.assert_called_once_with(1) + assert attr.get() == 1 class SimpleUpdater(AttrHandlerR): From 16c0ca92bb9001ece0eafdd065ffd2455dcdd10c Mon Sep 17 00:00:00 2001 From: Shihab Suliman Date: Mon, 11 Aug 2025 13:37:57 +0000 Subject: [PATCH 3/5] chore: remove import --- tests/test_attribute.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_attribute.py b/tests/test_attribute.py index 39d5bec77..325fca792 100644 --- a/tests/test_attribute.py +++ b/tests/test_attribute.py @@ -6,7 +6,6 @@ from fastcs.attributes import ( AttrHandlerR, AttrHandlerRW, - AttrHandlerW, AttrR, AttrRW, AttrW, From bb775fe775391c7d233f9ae436ba5d2dfbe1e3c1 Mon Sep 17 00:00:00 2001 From: Shihab Suliman Date: Mon, 11 Aug 2025 14:56:17 +0000 Subject: [PATCH 4/5] tests: add handler for attrW in example ioc --- tests/example_p4p_ioc.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/example_p4p_ioc.py b/tests/example_p4p_ioc.py index 5fff09915..d7ce94745 100644 --- a/tests/example_p4p_ioc.py +++ b/tests/example_p4p_ioc.py @@ -3,7 +3,7 @@ import numpy as np -from fastcs.attributes import AttrR, AttrRW, AttrW +from fastcs.attributes import AttrHandlerW, AttrR, AttrRW, AttrW from fastcs.controller import Controller, SubController from fastcs.datatypes import Bool, Enum, Float, Int, Table, Waveform from fastcs.launch import FastCS @@ -14,6 +14,11 @@ from fastcs.wrappers import command, scan +class SimpleAttributeSetter(AttrHandlerW): + async def put(self, attr, value): + await attr.update_display_without_process(value) + + class FEnum(enum.Enum): A = 0 B = 1 @@ -25,7 +30,7 @@ class FEnum(enum.Enum): class ParentController(Controller): description = "some controller" a: AttrRW = AttrRW(Int(max=400_000, max_alarm=40_000)) - b: AttrW = AttrW(Float(min=-1, min_alarm=-0.5)) + b: AttrW = AttrW(Float(min=-1, min_alarm=-0.5), handler=SimpleAttributeSetter()) table: AttrRW = AttrRW( Table([("A", np.int32), ("B", "i"), ("C", "?"), ("D", np.float64)]) @@ -34,7 +39,7 @@ class ParentController(Controller): class ChildController(SubController): fail_on_next_e = True - c: AttrW = AttrW(Int()) + c: AttrW = AttrW(Int(), handler=SimpleAttributeSetter()) @command() async def d(self): From d259544b7b7c925d6462fb0d24624426e82e4230 Mon Sep 17 00:00:00 2001 From: Shihab Suliman Date: Wed, 13 Aug 2025 14:27:36 +0000 Subject: [PATCH 5/5] refactor: add instance check back to simpler handler put --- src/fastcs/attributes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fastcs/attributes.py b/src/fastcs/attributes.py index 63fb56a17..160360db4 100644 --- a/src/fastcs/attributes.py +++ b/src/fastcs/attributes.py @@ -49,10 +49,12 @@ class AttrHandlerRW(AttrHandlerR, AttrHandlerW): class SimpleAttrHandler(AttrHandlerRW): """Handler for internal parameters""" - async def put(self, attr: AttrRW[T], value: T) -> None: # type: ignore - await attr.set(value) + async def put(self, attr: AttrW[T], value: T) -> None: await attr.update_display_without_process(value) + if isinstance(attr, AttrRW): + await attr.set(value) + async def update(self, attr: AttrR) -> None: raise RuntimeError("SimpleHandler cannot update")