From b6b07ec0d90a2690f2114d11b3fdcd084f9d841b Mon Sep 17 00:00:00 2001 From: guillaumepichon Date: Thu, 22 Jan 2026 14:43:04 +0100 Subject: [PATCH 1/3] Ranges and availability for multiattribute. --- pyproject.toml | 2 +- tango/pyaml/multi_attribute.py | 20 +++++++++++++++++++- tests/conftest.py | 15 +++++++++++++++ tests/test_multi_attribute.py | 15 ++++++++++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2810022..39cb6a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ classifiers = [ dependencies = [ "PyTango>=10.1.1", - "accelerator-middle-layer<=0.2.2", + "accelerator-middle-layer>=0.2.2", "pydantic>=2.0" ] diff --git a/tango/pyaml/multi_attribute.py b/tango/pyaml/multi_attribute.py index 25ca05c..aebceb1 100644 --- a/tango/pyaml/multi_attribute.py +++ b/tango/pyaml/multi_attribute.py @@ -1,4 +1,5 @@ import logging +from typing import Tuple, Optional import numpy as np import pyaml @@ -28,11 +29,14 @@ class ConfigModel(BaseModel): Group name. unit : str, optional Unit of the attributes. + range : tuple(min, max), optional + Range of valid values. Use null for -∞ or +∞. """ attributes: list[str] = [] name: str = "" unit: str = "" + range: Optional[Tuple[Optional[float], Optional[float]]] = None class MultiAttribute(DeviceAccessList): @@ -41,7 +45,7 @@ def __init__(self, cfg: ConfigModel = None): self._cfg = cfg if self._cfg: for attribute in self._cfg.attributes: - attr_config = AttrConfig(attribute=attribute, unit=self._cfg.unit) + attr_config = AttrConfig(attribute=attribute, unit=self._cfg.unit, range=self._cfg.range) attr = Attribute(attr_config) self.append(attr) @@ -128,6 +132,20 @@ def readback(self) -> np.array: return np.array(values) + def get_range(self) -> list[float]: + attr_range: list[float] = [] + for device in self: + attr_range.extend(device.get_range()) + return attr_range + + def check_device_availability(self) -> bool: + available = False + for device in self: + available = device.check_device_availability() + if not available: + break + return available + def unit(self) -> str: if self._cfg: return self._cfg.unit diff --git a/tests/conftest.py b/tests/conftest.py index ac09f9e..e12bd02 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -74,6 +74,21 @@ def config_multi(): return MultiAttrCM(**cfg_dict) +@pytest.fixture +def config_multi_range(): + conf = """ +attributes: + - "sys/tg_test/1/float_scalar" + - "sys/tg_test/2/float_scalar" + - "sys/tg_test/3/float_scalar" + - "sys/tg_test/4/float_scalar" +unit: "A" +range: [-15, 15] +""" + cfg_dict = yaml.safe_load(conf) + return MultiAttrCM(**cfg_dict) + + @pytest.fixture def config_tango_cs(): conf = """ diff --git a/tests/test_multi_attribute.py b/tests/test_multi_attribute.py index a56a748..ce7398e 100644 --- a/tests/test_multi_attribute.py +++ b/tests/test_multi_attribute.py @@ -1,6 +1,8 @@ import random - +import pytest +from pyaml import PyAMLException +from tango.pyaml.attribute import Attribute from .mocked_control_system_initialized import MockedControlSystemInitialized from .mocked_device_proxy import MockedDeviceProxy from unittest.mock import patch @@ -24,3 +26,14 @@ def test_multi_read_write(self, config_multi): assert len(vals) == len(values) for index, val in enumerate(vals): assert val == values[index] + + def test_multiattribute_range(self, config_multi_range): + with ( + patch("tango.DeviceProxy", new=MockedDeviceProxy), + patch("tango.pyaml.controlsystem.TangoControlSystem", new=MockedControlSystemInitialized), + ): + ma = MultiAttribute(config_multi_range) + attr_range = ma.get_range() + assert attr_range is not None + assert len(attr_range) == 8 # (4*2) + assert attr_range == [-15, 15,-15, 15,-15, 15,-15, 15] From ab44ecfaa5d16513ba7fcf0b37121a58f07ca0df Mon Sep 17 00:00:00 2001 From: guillaumepichon Date: Thu, 22 Jan 2026 14:46:00 +0100 Subject: [PATCH 2/3] Ruff formatting --- tango/pyaml/multi_attribute.py | 4 +++- tests/test_multi_attribute.py | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tango/pyaml/multi_attribute.py b/tango/pyaml/multi_attribute.py index aebceb1..d6e289d 100644 --- a/tango/pyaml/multi_attribute.py +++ b/tango/pyaml/multi_attribute.py @@ -45,7 +45,9 @@ def __init__(self, cfg: ConfigModel = None): self._cfg = cfg if self._cfg: for attribute in self._cfg.attributes: - attr_config = AttrConfig(attribute=attribute, unit=self._cfg.unit, range=self._cfg.range) + attr_config = AttrConfig( + attribute=attribute, unit=self._cfg.unit, range=self._cfg.range + ) attr = Attribute(attr_config) self.append(attr) diff --git a/tests/test_multi_attribute.py b/tests/test_multi_attribute.py index ce7398e..4185f03 100644 --- a/tests/test_multi_attribute.py +++ b/tests/test_multi_attribute.py @@ -1,8 +1,5 @@ import random -import pytest -from pyaml import PyAMLException -from tango.pyaml.attribute import Attribute from .mocked_control_system_initialized import MockedControlSystemInitialized from .mocked_device_proxy import MockedDeviceProxy from unittest.mock import patch @@ -30,10 +27,13 @@ def test_multi_read_write(self, config_multi): def test_multiattribute_range(self, config_multi_range): with ( patch("tango.DeviceProxy", new=MockedDeviceProxy), - patch("tango.pyaml.controlsystem.TangoControlSystem", new=MockedControlSystemInitialized), + patch( + "tango.pyaml.controlsystem.TangoControlSystem", + new=MockedControlSystemInitialized, + ), ): ma = MultiAttribute(config_multi_range) attr_range = ma.get_range() assert attr_range is not None - assert len(attr_range) == 8 # (4*2) - assert attr_range == [-15, 15,-15, 15,-15, 15,-15, 15] + assert len(attr_range) == 8 # (4*2) + assert attr_range == [-15, 15, -15, 15, -15, 15, -15, 15] From 81a7cc6ad437b3125d77a3b48166d7926013e2dd Mon Sep 17 00:00:00 2001 From: guillaumepichon Date: Fri, 23 Jan 2026 11:40:48 +0100 Subject: [PATCH 3/3] Patch version bump --- tango/pyaml/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tango/pyaml/__init__.py b/tango/pyaml/__init__.py index 096e4f9..ad4a188 100644 --- a/tango/pyaml/__init__.py +++ b/tango/pyaml/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.3.2" +__version__ = "0.3.3" import logging.config import os