From 3eb02cc616aa76110f046b7f959b60164e4b6849 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 13 Feb 2026 15:43:27 +0000 Subject: [PATCH 1/5] Pass attribute instead of datatype into _get_read_widget --- src/fastcs/transports/epics/gui.py | 13 ++++++++----- src/fastcs/transports/epics/pva/gui.py | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/fastcs/transports/epics/gui.py b/src/fastcs/transports/epics/gui.py index 9bc47f7b..d4ce6e3e 100644 --- a/src/fastcs/transports/epics/gui.py +++ b/src/fastcs/transports/epics/gui.py @@ -57,8 +57,8 @@ def _get_pv(self, attr_path: list[str], name: str): ) return f"{attr_prefix}:{snake_to_pascal(name)}" - def _get_read_widget(self, fastcs_datatype: DataType) -> ReadWidgetUnion | None: - match fastcs_datatype: + def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: + match attribute.datatype: case Bool(): return LED() case Int(): @@ -71,7 +71,10 @@ def _get_read_widget(self, fastcs_datatype: DataType) -> ReadWidgetUnion | None: return TextRead(format=TextFormat.string) case Waveform() as waveform: if len(waveform.shape) > 1: - logger.warning("EPICS CA transport only supports 1D waveforms") + logger.warning( + "EPICS CA transport only supports 1D waveforms," + + f"{attribute} is a {waveform.shape} waveform" + ) return None return ArrayTrace(axis="x") @@ -102,7 +105,7 @@ def _get_attribute_component( name = snake_to_pascal(name) match attribute: case AttrRW(): - read_widget = self._get_read_widget(attribute.datatype) + read_widget = self._get_read_widget(attribute) write_widget = self._get_write_widget(attribute.datatype) if write_widget is None or read_widget is None: return None @@ -115,7 +118,7 @@ def _get_attribute_component( read_widget=read_widget, ) case AttrR(): - read_widget = self._get_read_widget(attribute.datatype) + read_widget = self._get_read_widget(attribute) if read_widget is None: return None return SignalR( diff --git a/src/fastcs/transports/epics/pva/gui.py b/src/fastcs/transports/epics/pva/gui.py index b305b981..e0d4f2cc 100644 --- a/src/fastcs/transports/epics/pva/gui.py +++ b/src/fastcs/transports/epics/pva/gui.py @@ -8,6 +8,8 @@ WriteWidgetUnion, ) +from fastcs.attributes.attr_r import AttrR +from fastcs.attributes.attribute import Attribute from fastcs.datatypes import Bool, DataType, Table, Waveform, numpy_to_fastcs_datatype from fastcs.transports.epics.gui import EpicsGUI @@ -20,17 +22,18 @@ class PvaEpicsGUI(EpicsGUI): def _get_pv(self, attr_path: list[str], name: str): return f"pva://{super()._get_pv(attr_path, name)}" - def _get_read_widget(self, fastcs_datatype: DataType) -> ReadWidgetUnion | None: - match fastcs_datatype: + def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: + match attribute.datatype: case Table(): fastcs_datatypes = [ numpy_to_fastcs_datatype(datatype) - for _, datatype in fastcs_datatype.structured_dtype + for _, datatype in attribute.datatype.structured_dtype ] base_get_read_widget = super()._get_read_widget widgets = [ - base_get_read_widget(datatype) for datatype in fastcs_datatypes + base_get_read_widget(AttrR(datatype)) + for datatype in fastcs_datatypes ] return TableRead(widgets=widgets) # type: ignore @@ -39,7 +42,7 @@ def _get_read_widget(self, fastcs_datatype: DataType) -> ReadWidgetUnion | None: height=height, width=width, color_map=ImageColorMap.GRAY ) case _: - return super()._get_read_widget(fastcs_datatype) + return super()._get_read_widget(attribute) def _get_write_widget(self, fastcs_datatype: DataType) -> WriteWidgetUnion | None: match fastcs_datatype: From c8d94a8004352b4497463ee90078507af2049cfb Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 19 Feb 2026 10:53:37 +0000 Subject: [PATCH 2/5] Fix log message --- src/fastcs/transports/epics/gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fastcs/transports/epics/gui.py b/src/fastcs/transports/epics/gui.py index d4ce6e3e..9e541867 100644 --- a/src/fastcs/transports/epics/gui.py +++ b/src/fastcs/transports/epics/gui.py @@ -72,8 +72,8 @@ def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: case Waveform() as waveform: if len(waveform.shape) > 1: logger.warning( - "EPICS CA transport only supports 1D waveforms," - + f"{attribute} is a {waveform.shape} waveform" + "EPICS CA transport only supports 1D waveforms, " + f"{attribute} is a {waveform.shape}D waveform" ) return None From 0e0e960095c9bd77402bef93934c203c93a90280 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 19 Feb 2026 11:20:46 +0000 Subject: [PATCH 3/5] Pass attribute instead of datatype into _get_write_widget --- src/fastcs/transports/epics/gui.py | 11 +++++------ src/fastcs/transports/epics/pva/gui.py | 13 +++++++------ tests/transports/epics/ca/test_gui.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/fastcs/transports/epics/gui.py b/src/fastcs/transports/epics/gui.py index 9e541867..7a04c9f8 100644 --- a/src/fastcs/transports/epics/gui.py +++ b/src/fastcs/transports/epics/gui.py @@ -26,7 +26,6 @@ from fastcs.attributes import Attribute, AttrR, AttrRW, AttrW from fastcs.datatypes import ( Bool, - DataType, Enum, Float, Int, @@ -81,8 +80,8 @@ def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: case datatype: raise TypeError(f"Unsupported type {type(datatype)}: {datatype}") - def _get_write_widget(self, fastcs_datatype: DataType) -> WriteWidgetUnion | None: - match fastcs_datatype: + def _get_write_widget(self, attribute: Attribute) -> WriteWidgetUnion | None: + match attribute.datatype: case Bool(): return ToggleButton() case Int(): @@ -92,7 +91,7 @@ def _get_write_widget(self, fastcs_datatype: DataType) -> WriteWidgetUnion | Non case String(): return TextWrite(format=TextFormat.string) case Enum(): - return ComboBox(choices=fastcs_datatype.names) + return ComboBox(choices=attribute.datatype.names) case Waveform(): return None case datatype: @@ -106,7 +105,7 @@ def _get_attribute_component( match attribute: case AttrRW(): read_widget = self._get_read_widget(attribute) - write_widget = self._get_write_widget(attribute.datatype) + write_widget = self._get_write_widget(attribute) if write_widget is None or read_widget is None: return None return SignalRW( @@ -128,7 +127,7 @@ def _get_attribute_component( read_widget=read_widget, ) case AttrW(): - write_widget = self._get_write_widget(attribute.datatype) + write_widget = self._get_write_widget(attribute) if write_widget is None: return None return SignalW( diff --git a/src/fastcs/transports/epics/pva/gui.py b/src/fastcs/transports/epics/pva/gui.py index e0d4f2cc..6032497d 100644 --- a/src/fastcs/transports/epics/pva/gui.py +++ b/src/fastcs/transports/epics/pva/gui.py @@ -10,7 +10,7 @@ from fastcs.attributes.attr_r import AttrR from fastcs.attributes.attribute import Attribute -from fastcs.datatypes import Bool, DataType, Table, Waveform, numpy_to_fastcs_datatype +from fastcs.datatypes import Bool, Table, Waveform, numpy_to_fastcs_datatype from fastcs.transports.epics.gui import EpicsGUI @@ -44,18 +44,19 @@ def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: case _: return super()._get_read_widget(attribute) - def _get_write_widget(self, fastcs_datatype: DataType) -> WriteWidgetUnion | None: - match fastcs_datatype: + def _get_write_widget(self, attribute: Attribute) -> WriteWidgetUnion | None: + datatype = attribute.datatype + match attribute.datatype: case Table(): widgets = [] - for _, datatype in fastcs_datatype.structured_dtype: + for _, datatype in attribute.datatype.structured_dtype: fastcs_datatype = numpy_to_fastcs_datatype(datatype) if isinstance(fastcs_datatype, Bool): # Replace with compact version for Table row widget = CheckBox() else: - widget = super()._get_write_widget(fastcs_datatype) + widget = super()._get_write_widget(AttrR(fastcs_datatype)) widgets.append(widget) return TableWrite(widgets=widgets) case _: - return super()._get_write_widget(fastcs_datatype) + return super()._get_write_widget(attribute) diff --git a/tests/transports/epics/ca/test_gui.py b/tests/transports/epics/ca/test_gui.py index 68ea0d83..9c422ca6 100644 --- a/tests/transports/epics/ca/test_gui.py +++ b/tests/transports/epics/ca/test_gui.py @@ -93,7 +93,7 @@ def test_get_attribute_component_none(mocker): def test_get_write_widget_none(): gui = EpicsGUI(ControllerAPI(), "DEVICE") - assert gui._get_write_widget(fastcs_datatype=Waveform(np.int32)) is None + assert gui._get_write_widget(attribute=AttrR(Waveform(np.int32))) is None def test_get_components(controller_api): From dfa17b07202b53543e8bb94430149cadb575fde7 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 19 Feb 2026 13:36:08 +0000 Subject: [PATCH 4/5] Fixes --- src/fastcs/transports/epics/gui.py | 2 +- src/fastcs/transports/epics/pva/gui.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fastcs/transports/epics/gui.py b/src/fastcs/transports/epics/gui.py index 7a04c9f8..0e650cf2 100644 --- a/src/fastcs/transports/epics/gui.py +++ b/src/fastcs/transports/epics/gui.py @@ -72,7 +72,7 @@ def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: if len(waveform.shape) > 1: logger.warning( "EPICS CA transport only supports 1D waveforms, " - f"{attribute} is a {waveform.shape}D waveform" + f"{attribute} is a {len(waveform.shape)}D waveform" ) return None diff --git a/src/fastcs/transports/epics/pva/gui.py b/src/fastcs/transports/epics/pva/gui.py index 6032497d..dd5ebf96 100644 --- a/src/fastcs/transports/epics/pva/gui.py +++ b/src/fastcs/transports/epics/pva/gui.py @@ -45,7 +45,6 @@ def _get_read_widget(self, attribute: Attribute) -> ReadWidgetUnion | None: return super()._get_read_widget(attribute) def _get_write_widget(self, attribute: Attribute) -> WriteWidgetUnion | None: - datatype = attribute.datatype match attribute.datatype: case Table(): widgets = [] From e0b86cc9742cfdf77b4f11877d04e744799d54e2 Mon Sep 17 00:00:00 2001 From: Gary Yendell Date: Fri, 20 Feb 2026 14:01:16 +0000 Subject: [PATCH 5/5] Fix coderabbit nitpicks --- src/fastcs/transports/epics/pva/gui.py | 5 ++--- tests/transports/epics/ca/test_gui.py | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fastcs/transports/epics/pva/gui.py b/src/fastcs/transports/epics/pva/gui.py index dd5ebf96..0ae6e4de 100644 --- a/src/fastcs/transports/epics/pva/gui.py +++ b/src/fastcs/transports/epics/pva/gui.py @@ -8,8 +8,7 @@ WriteWidgetUnion, ) -from fastcs.attributes.attr_r import AttrR -from fastcs.attributes.attribute import Attribute +from fastcs.attributes import Attribute, AttrR, AttrW from fastcs.datatypes import Bool, Table, Waveform, numpy_to_fastcs_datatype from fastcs.transports.epics.gui import EpicsGUI @@ -54,7 +53,7 @@ def _get_write_widget(self, attribute: Attribute) -> WriteWidgetUnion | None: # Replace with compact version for Table row widget = CheckBox() else: - widget = super()._get_write_widget(AttrR(fastcs_datatype)) + widget = super()._get_write_widget(AttrW(fastcs_datatype)) widgets.append(widget) return TableWrite(widgets=widgets) case _: diff --git a/tests/transports/epics/ca/test_gui.py b/tests/transports/epics/ca/test_gui.py index 9c422ca6..296be411 100644 --- a/tests/transports/epics/ca/test_gui.py +++ b/tests/transports/epics/ca/test_gui.py @@ -93,7 +93,9 @@ def test_get_attribute_component_none(mocker): def test_get_write_widget_none(): gui = EpicsGUI(ControllerAPI(), "DEVICE") - assert gui._get_write_widget(attribute=AttrR(Waveform(np.int32))) is None + assert ( + gui._get_write_widget(attribute=AttrR(Waveform(array_dtype=np.int32))) is None + ) def test_get_components(controller_api):