From f66d403040e1d9c90502e0f5dcb866711729ac2d Mon Sep 17 00:00:00 2001 From: Devin Burke Date: Thu, 12 Sep 2024 13:42:40 +0200 Subject: [PATCH] When creating a tango device, the signal type is now required in type annotations unless it is a SignalX. Annotations may also include Devices or DeviceVectors. The kwargs for these objects should be passed to init for the device as a dict called . Signals which are not explicitly annotated are automatically filled in as attributes when the device is connected. Their signal read/write character and datatype is inferred from the tango server. --- src/ophyd_async/tango/__init__.py | 4 +- .../tango/base_devices/_base_device.py | 164 +++++++++++++----- src/ophyd_async/tango/demo/_counter.py | 11 +- src/ophyd_async/tango/demo/_mover.py | 16 +- src/ophyd_async/tango/signal/__init__.py | 4 +- src/ophyd_async/tango/signal/_signal.py | 97 ++++++----- tests/tango/test_base_device.py | 46 ++--- tests/tango/test_tango_signals.py | 67 ++----- 8 files changed, 229 insertions(+), 180 deletions(-) diff --git a/src/ophyd_async/tango/__init__.py b/src/ophyd_async/tango/__init__.py index 3f8ef2d0d9..371a590ab5 100644 --- a/src/ophyd_async/tango/__init__.py +++ b/src/ophyd_async/tango/__init__.py @@ -13,7 +13,7 @@ get_tango_trl, get_trl_descriptor, infer_python_type, - infer_signal_frontend, + infer_signal_character, make_backend, tango_signal_auto, tango_signal_r, @@ -32,7 +32,7 @@ get_trl_descriptor, get_tango_trl, infer_python_type, - infer_signal_frontend, + infer_signal_character, make_backend, AttributeProxy, CommandProxy, diff --git a/src/ophyd_async/tango/base_devices/_base_device.py b/src/ophyd_async/tango/base_devices/_base_device.py index 8c28f2530e..3e03108597 100644 --- a/src/ophyd_async/tango/base_devices/_base_device.py +++ b/src/ophyd_async/tango/base_devices/_base_device.py @@ -1,20 +1,32 @@ from __future__ import annotations -from typing import Dict, Optional, Tuple, Union, get_type_hints +from typing import ( + Dict, + Optional, + Tuple, + Type, + TypeVar, + Union, + get_args, + get_origin, + get_type_hints, +) from ophyd_async.core import ( DEFAULT_TIMEOUT, Device, + DeviceVector, Signal, ) from ophyd_async.tango.signal import ( - infer_python_type, - infer_signal_frontend, make_backend, + tango_signal_auto, ) from tango import DeviceProxy as SyncDeviceProxy from tango.asyncio import DeviceProxy as AsyncDeviceProxy +T = TypeVar("T") + class TangoDevice(Device): """ @@ -35,6 +47,7 @@ class TangoDevice(Device): proxy: Optional[Union[AsyncDeviceProxy, SyncDeviceProxy]] = None _polling: Tuple[bool, float, float, float] = (False, 0.1, None, 0.1) _signal_polling: Dict[str, Tuple[bool, float, float, float]] = {} + _poll_only_annotated_signals: bool = True def __init__( self, @@ -47,8 +60,7 @@ def __init__( self.trl = trl if trl else "" self.proxy = device_proxy - - self.create_children_from_annotations() + tango_create_children_from_annotations(self) super().__init__(name=name) async def connect( @@ -70,46 +82,29 @@ async def closure(): await closure() self.register_signals() + await fill_proxy_entries(self) + # set_name should be called again to propagate the new signal names self.set_name(self.name) + # Set the polling configuration + if self._polling[0]: + for child in self.children(): + if issubclass(type(child[1]), Signal): + child[1]._backend.set_polling(*self._polling) # noqa: SLF001 + child[1]._backend.allow_events(False) # noqa: SLF001 + if self._signal_polling: + for signal_name, polling in self._signal_polling.items(): + if hasattr(self, signal_name): + attr = getattr(self, signal_name) + attr._backend.set_polling(*polling) # noqa: SLF001 + attr._backend.allow_events(False) # noqa: SLF001 + await super().connect(mock=mock, timeout=timeout) + # Users can override this method to register new signals def register_signals(self): - annots = get_type_hints(self.__class__) - for name, obj_type in annots.items(): - if hasattr(self, name): - signal = getattr(self, name) - if issubclass(type(signal), Signal): - tango_name = name.lstrip("_") - read_trl = f"{self.trl}/{tango_name}" - datatype = infer_python_type(read_trl) - backend = make_backend( - datatype=datatype, - read_trl=read_trl, - write_trl=read_trl, - device_proxy=self.proxy, - ) - if self._polling[0]: - backend.allow_events(False) - backend.set_polling(*self._polling) - if name in self._signal_polling: - backend.allow_events(False) - backend.set_polling(*self._signal_polling[name]) - signal._backend = backend # noqa: SLF001 - - def create_children_from_annotations(self): - annots = get_type_hints(self.__class__) - for attr_name, obj_type in annots.items(): - if isinstance(obj_type, type): - if obj_type is Signal: - tango_name = attr_name.lstrip("_") - trl = f"{self.trl}/{tango_name}" - setattr( - self, attr_name, infer_signal_frontend(trl=trl, name=attr_name) - ) - elif issubclass(obj_type, Signal): - setattr(self, attr_name, obj_type(name=attr_name)) + pass def tango_polling( @@ -170,3 +165,94 @@ def decorator(cls): return cls return decorator + + +def tango_create_children_from_annotations( + device: TangoDevice, + included_optional_fields: Tuple[str, ...] = (), + device_vectors: Optional[Dict[str, int]] = None, +): + """Initialize blocks at __init__ of `device`.""" + for name, device_type in get_type_hints(type(device)).items(): + if name in ("_name", "parent"): + continue + + device_type, is_optional = _strip_union(device_type) + if is_optional and name not in included_optional_fields: + continue + + is_device_vector, device_type = _strip_device_vector(device_type) + if is_device_vector: + kwargs = "_" + name + "_kwargs" + kwargs = getattr(device, kwargs, {}) + prefix = kwargs["prefix"] + count = kwargs["count"] + n_device_vector = DeviceVector( + {i: device_type(f"{prefix}{i}") for i in range(1, count + 1)} + ) + setattr(device, name, n_device_vector) + + else: + origin = get_origin(device_type) + origin = origin if origin else device_type + + if issubclass(origin, Signal): + datatype = None + tango_name = name.lstrip("_") + read_trl = f"{device.trl}/{tango_name}" + type_args = get_args(device_type) + if type_args: + datatype = type_args[0] + backend = make_backend( + datatype=datatype, + read_trl=read_trl, + write_trl=read_trl, + device_proxy=device.proxy, + ) + setattr(device, name, origin(name=name, backend=backend)) + + elif issubclass(origin, Device) or isinstance(origin, Device): + kwargs = "_" + name + "_kwargs" + kwargs = getattr(device, kwargs, "") + setattr(device, name, origin(**kwargs)) + + +async def fill_proxy_entries(device: TangoDevice): + proxy_trl = device.trl + children = [name.lstrip("_") for name, _ in device.children()] + proxy_attributes = list(device.proxy.get_attribute_list()) + proxy_commands = list(device.proxy.get_command_list()) + combined = proxy_attributes + proxy_commands + + for name in combined: + if name not in children: + full_trl = f"{proxy_trl}/{name}" + try: + auto_signal = await tango_signal_auto( + trl=full_trl, device_proxy=device.proxy + ) + setattr(device, name, auto_signal) + except RuntimeError as e: + if "Commands with different in and out dtypes" in str(e): + print( + f"Skipping {name}. Commands with different in and out dtypes" + f" are not supported." + ) + continue + raise e + + +def _strip_union(field: Union[Union[T], T]) -> Tuple[T, bool]: + if get_origin(field) is Union: + args = get_args(field) + is_optional = type(None) in args + for arg in args: + if arg is not type(None): + return arg, is_optional + return field, False + + +def _strip_device_vector(field: Union[Type[Device]]) -> Tuple[bool, Type[Device]]: + if get_origin(field) is DeviceVector: + return True, get_args(field)[0] + return False, field diff --git a/src/ophyd_async/tango/demo/_counter.py b/src/ophyd_async/tango/demo/_counter.py index 6280358a47..1a8ff7dc3d 100644 --- a/src/ophyd_async/tango/demo/_counter.py +++ b/src/ophyd_async/tango/demo/_counter.py @@ -6,7 +6,8 @@ AsyncStatus, ConfigSignal, HintedSignal, - Signal, + SignalR, + SignalRW, SignalX, ) from ophyd_async.tango import TangoReadable, tango_polling @@ -23,15 +24,13 @@ class TangoCounterConfig: class TangoCounter(TangoReadable): # Enter the name and type of the signals you want to use # If type is None or Signal, the type will be inferred from the Tango device - counts: Signal - sample_time: Signal - state: Signal - reset: Signal + counts: SignalR[int] + sample_time: SignalRW[float] start: SignalX def __init__(self, trl: str, name=""): super().__init__(trl, name=name) - self.add_readables([self.counts], HintedSignal.uncached) + self.add_readables([self.counts], HintedSignal) self.add_readables([self.sample_time], ConfigSignal) @AsyncStatus.wrap diff --git a/src/ophyd_async/tango/demo/_mover.py b/src/ophyd_async/tango/demo/_mover.py index f491a31912..bf54f6b014 100644 --- a/src/ophyd_async/tango/demo/_mover.py +++ b/src/ophyd_async/tango/demo/_mover.py @@ -11,7 +11,6 @@ CalculateTimeout, ConfigSignal, HintedSignal, - SignalR, SignalRW, SignalX, WatchableAsyncStatus, @@ -32,9 +31,8 @@ class TangoMoverConfig: class TangoMover(TangoReadable, Movable, Stoppable): # Enter the name and type of the signals you want to use # If type is None or Signal, the type will be inferred from the Tango device - position: SignalRW - velocity: SignalRW - state: SignalR + position: SignalRW[float] + velocity: SignalRW[float] _stop: SignalX def __init__(self, trl: str, name=""): @@ -57,7 +55,7 @@ async def set(self, value: float, timeout: CalculatableTimeout = CalculateTimeou await self.position.set(value, wait=False, timeout=timeout) # Wait for the motor to stop - move_status = AsyncStatus(self._wait_for_idle()) + move_status = self.wait_for_idle() try: async for current_position in observe_value( @@ -75,11 +73,8 @@ async def set(self, value: float, timeout: CalculatableTimeout = CalculateTimeou if not self._set_success: raise RuntimeError("Motor was stopped") - async def _wait_for_idle(self): - if self.state._backend.support_events is False: # noqa: SLF001 - if self.state._backend._polling[0] is False: # noqa: SLF001 - raise RuntimeError("State does not support events or polling") - + @AsyncStatus.wrap + async def wait_for_idle(self): event = asyncio.Event() def _wait(value: dict[str, Reading]): @@ -88,6 +83,7 @@ def _wait(value: dict[str, Reading]): self.state.subscribe(_wait) await event.wait() + self.state.clear_sub(_wait) def stop(self, success: bool = True) -> AsyncStatus: self._set_success = success diff --git a/src/ophyd_async/tango/signal/__init__.py b/src/ophyd_async/tango/signal/__init__.py index bf8abbcbba..a285e34c03 100644 --- a/src/ophyd_async/tango/signal/__init__.py +++ b/src/ophyd_async/tango/signal/__init__.py @@ -1,6 +1,6 @@ from ._signal import ( infer_python_type, - infer_signal_frontend, + infer_signal_character, make_backend, tango_signal_auto, tango_signal_r, @@ -29,7 +29,7 @@ "get_trl_descriptor", "get_tango_trl", "infer_python_type", - "infer_signal_frontend", + "infer_signal_character", "make_backend", "tango_signal_r", "tango_signal_rw", diff --git a/src/ophyd_async/tango/signal/_signal.py b/src/ophyd_async/tango/signal/_signal.py index 3febeb164b..2395e96eaf 100644 --- a/src/ophyd_async/tango/signal/_signal.py +++ b/src/ophyd_async/tango/signal/_signal.py @@ -13,7 +13,6 @@ get_python_type, ) from tango import AttrDataFormat, AttrWriteType, CmdArgType, DevState -from tango import DeviceProxy as SyncDeviceProxy from tango.asyncio import DeviceProxy @@ -27,8 +26,7 @@ def make_backend( def tango_signal_rw( - datatype: Optional[Type[T]] = None, - *, + datatype: Type[T], read_trl: str, write_trl: Optional[str] = None, device_proxy: Optional[DeviceProxy] = None, @@ -52,15 +50,12 @@ def tango_signal_rw( name: The name of the Signal """ - if datatype is None: - datatype = infer_python_type(read_trl) backend = make_backend(datatype, read_trl, write_trl or read_trl, device_proxy) return SignalRW(backend, timeout=timeout, name=name) def tango_signal_r( - datatype: Optional[Type[T]] = None, - *, + datatype: Type[T], read_trl: str, device_proxy: Optional[DeviceProxy] = None, timeout: float = DEFAULT_TIMEOUT, @@ -81,15 +76,12 @@ def tango_signal_r( name: The name of the Signal """ - if datatype is None: - datatype = infer_python_type(read_trl) backend = make_backend(datatype, read_trl, read_trl, device_proxy) return SignalR(backend, timeout=timeout, name=name) def tango_signal_w( - datatype: Optional[Type[T]] = None, - *, + datatype: Type[T], write_trl: str, device_proxy: Optional[DeviceProxy] = None, timeout: float = DEFAULT_TIMEOUT, @@ -110,8 +102,6 @@ def tango_signal_w( name: The name of the Signal """ - if datatype is None: - datatype = infer_python_type(write_trl) backend = make_backend(datatype, write_trl, write_trl, device_proxy) return SignalW(backend, timeout=timeout, name=name) @@ -139,32 +129,48 @@ def tango_signal_x( return SignalX(backend, timeout=timeout, name=name) -def tango_signal_auto( +async def tango_signal_auto( datatype: Optional[Type[T]] = None, *, trl: str, - device_proxy: Optional[DeviceProxy] = None, + device_proxy: Optional[DeviceProxy], timeout: float = DEFAULT_TIMEOUT, name: str = "", -) -> Union[SignalW, SignalX, SignalR, SignalRW]: +) -> Union[SignalW, SignalX, SignalR, SignalRW, None]: + try: + signal_character = await infer_signal_character(trl, device_proxy) + except RuntimeError as e: + if "Commands with different in and out dtypes" in str(e): + return None + else: + raise e + if datatype is None: - datatype = infer_python_type(trl) - backend = make_backend(datatype, trl, trl, device_proxy) - signal = infer_signal_frontend(trl, name, timeout) - signal._backend = backend # noqa: SLF001 + datatype = await infer_python_type(trl, device_proxy) - return signal + backend = make_backend(datatype, trl, trl, device_proxy) + if signal_character == "RW": + return SignalRW(backend=backend, timeout=timeout, name=name) + if signal_character == "R": + return SignalR(backend=backend, timeout=timeout, name=name) + if signal_character == "W": + return SignalW(backend=backend, timeout=timeout, name=name) + if signal_character == "X": + return SignalX(backend=backend, timeout=timeout, name=name) -def infer_python_type(trl: str) -> Type[T]: +async def infer_python_type(trl: str = "", proxy: DeviceProxy = None) -> Type[T]: device_trl, tr_name = trl.rsplit("/", 1) - syn_proxy = SyncDeviceProxy(device_trl) + if proxy is None: + dev_proxy = await DeviceProxy(device_trl) + else: + dev_proxy = proxy - if tr_name in syn_proxy.get_command_list(): - config = syn_proxy.get_command_config(tr_name) + if tr_name in dev_proxy.get_command_list(): + config = await dev_proxy.get_command_config(tr_name) isarray, py_type, _ = get_python_type(config.in_type) - elif tr_name in syn_proxy.get_attribute_list(): - config = syn_proxy.get_attribute_config(tr_name) + elif tr_name in dev_proxy.get_attribute_list(): + config = await dev_proxy.get_attribute_config(tr_name) isarray, py_type, _ = get_python_type(config.data_type) if py_type is Enum: enum_dict = {label: i for i, label in enumerate(config.enum_labels)} @@ -180,29 +186,36 @@ def infer_python_type(trl: str) -> Type[T]: return npt.NDArray[py_type] if isarray else py_type -def infer_signal_frontend(trl, name: str = "", timeout: float = DEFAULT_TIMEOUT): +async def infer_signal_character(trl, proxy: DeviceProxy = None) -> str: device_trl, tr_name = trl.rsplit("/", 1) - proxy = SyncDeviceProxy(device_trl) + if proxy is None: + dev_proxy = await DeviceProxy(device_trl) + else: + dev_proxy = proxy - if tr_name in proxy.get_pipe_list(): + if tr_name in dev_proxy.get_pipe_list(): raise NotImplementedError("Pipes are not supported") - if tr_name not in proxy.get_attribute_list(): - if tr_name not in proxy.get_command_list(): + if tr_name not in dev_proxy.get_attribute_list(): + if tr_name not in dev_proxy.get_command_list(): raise RuntimeError(f"Cannot find {tr_name} in {device_trl}") - if tr_name in proxy.get_attribute_list(): - config = proxy.get_attribute_config(tr_name) + if tr_name in dev_proxy.get_attribute_list(): + config = await dev_proxy.get_attribute_config(tr_name) if config.writable in [AttrWriteType.READ_WRITE, AttrWriteType.READ_WITH_WRITE]: - return SignalRW(name=name, timeout=timeout) + return "RW" elif config.writable == AttrWriteType.READ: - return SignalR(name=name, timeout=timeout) + return "R" else: - return SignalW(name=name, timeout=timeout) + return "W" - if tr_name in proxy.get_command_list(): - config = proxy.get_command_config(tr_name) + if tr_name in dev_proxy.get_command_list(): + config = await dev_proxy.get_command_config(tr_name) if config.in_type == CmdArgType.DevVoid: - return SignalX(name=name, timeout=timeout) - elif config.out_type != CmdArgType.DevVoid: - return SignalRW(name=name, timeout=timeout) + return "X" + elif config.in_type != config.out_type: + raise RuntimeError( + "Commands with different in and out dtypes are not" " supported" + ) + else: + return "RW" diff --git a/tests/tango/test_base_device.py b/tests/tango/test_base_device.py index a27ebba114..d47d62e5e7 100644 --- a/tests/tango/test_base_device.py +++ b/tests/tango/test_base_device.py @@ -6,12 +6,13 @@ import bluesky.plan_stubs as bps import bluesky.plans as bp import numpy as np +import numpy.typing as npt import pytest from bluesky import RunEngine import tango -from ophyd_async.core import DeviceCollector, T -from ophyd_async.tango import TangoReadable, get_python_type, tango_signal_auto +from ophyd_async.core import DeviceCollector, HintedSignal, SignalRW, T +from ophyd_async.tango import TangoReadable, get_python_type from ophyd_async.tango.demo import ( DemoCounter, DemoMover, @@ -176,6 +177,9 @@ def raise_exception_cmd(self): # -------------------------------------------------------------------- class TestTangoReadable(TangoReadable): __test__ = False + justvalue: SignalRW[int] + array: SignalRW[npt.NDArray[float]] + limitedvalue: SignalRW[float] def __init__( self, @@ -183,20 +187,10 @@ def __init__( device_proxy: Optional[Union[AsyncDeviceProxy, SyncDeviceProxy]] = None, name: str = "", ) -> None: - self.trl = trl - TangoReadable.__init__(self, trl, device_proxy, name) - - def register_signals(self): - for feature in TESTED_FEATURES: - setattr( - self, - feature, - tango_signal_auto(datatype=None, trl=f"{self.trl}/{feature}"), - ) - attr = getattr(self, feature) - attr._backend.allow_events(False) - attr._backend.set_polling(0.1, 0.1, 0.1) - self.add_readables([attr]) + super().__init__(trl, device_proxy, name=name) + self.add_readables( + [self.justvalue, self.array, self.limitedvalue], HintedSignal.uncached + ) # -------------------------------------------------------------------- @@ -349,25 +343,20 @@ async def test_connect_proxy(tango_test_device, proxy: Optional[bool]): await test_device.connect() assert isinstance(test_device.proxy, tango._tango.DeviceProxy) else: - proxy = SyncDeviceProxy(tango_test_device) - test_device = TestTangoReadable(device_proxy=proxy) - await test_device.connect() - assert isinstance(test_device.proxy, tango._tango.DeviceProxy) + proxy = None + with pytest.raises(ValueError) as excinfo: + test_device = TestTangoReadable(device_proxy=proxy) + assert "Either 'trl' or 'device_proxy' must be provided." in str(excinfo.value) # -------------------------------------------------------------------- @pytest.mark.asyncio async def test_with_bluesky(tango_test_device): - async def connect(): - async with DeviceCollector(): - device = TestTangoReadable(tango_test_device) - return device - - ophyd_dev = await connect() - # now let's do some bluesky stuff RE = RunEngine() - RE(bp.count([ophyd_dev], 1)) + with DeviceCollector(): + device = TestTangoReadable(tango_test_device) + RE(bp.count([device])) # -------------------------------------------------------------------- @@ -386,7 +375,6 @@ async def test_tango_demo(demo_test_context): await motor1.connect() await counter1.connect() await counter2.connect() - print("Polling:", motor1._polling) RE = RunEngine() diff --git a/tests/tango/test_tango_signals.py b/tests/tango/test_tango_signals.py index 8b9f11a036..136600b6d9 100644 --- a/tests/tango/test_tango_signals.py +++ b/tests/tango/test_tango_signals.py @@ -11,7 +11,7 @@ from bluesky.protocols import Reading from test_base_device import TestDevice -from ophyd_async.core import SignalBackend, SignalRW, SignalX, T +from ophyd_async.core import SignalBackend, SignalR, SignalRW, SignalW, SignalX, T from ophyd_async.tango import ( TangoSignalBackend, tango_signal_auto, @@ -411,7 +411,7 @@ async def test_backend_get_put_monitor_cmd( # -------------------------------------------------------------------- @pytest.mark.asyncio @pytest.mark.parametrize( - "pv, tango_type, d_format, py_type, initial_value, put_value, use_dtype, use_proxy", + "pv, tango_type, d_format, py_type, initial_value, put_value, use_proxy", [ ( pv, @@ -420,7 +420,6 @@ async def test_backend_get_put_monitor_cmd( py_type, initial_value, put_value, - use_dtype, use_proxy, ) for ( @@ -431,15 +430,9 @@ async def test_backend_get_put_monitor_cmd( initial_value, put_value, ) in ATTRIBUTES_SET - for use_dtype in [True, False] - for use_proxy in [True, False] - ], - ids=[ - f"{x[0]}_{use_dtype}_{use_proxy}" - for x in ATTRIBUTES_SET - for use_dtype in [True, False] for use_proxy in [True, False] ], + ids=[f"{x[0]}_{use_proxy}" for x in ATTRIBUTES_SET for use_proxy in [True, False]], ) async def test_tango_signal_r( echo_device: str, @@ -449,13 +442,11 @@ async def test_tango_signal_r( py_type: Type[T], initial_value: T, put_value: T, - use_dtype: bool, use_proxy: bool, ): await prepare_device(echo_device, pv, initial_value) source = echo_device + "/" + pv proxy = await DeviceProxy(echo_device) if use_proxy else None - py_type = py_type if use_dtype else None timeout = 0.2 signal = tango_signal_r( @@ -473,7 +464,7 @@ async def test_tango_signal_r( # -------------------------------------------------------------------- @pytest.mark.asyncio @pytest.mark.parametrize( - "pv, tango_type, d_format, py_type, initial_value, put_value, use_dtype, use_proxy", + "pv, tango_type, d_format, py_type, initial_value, put_value, use_proxy", [ ( pv, @@ -482,7 +473,6 @@ async def test_tango_signal_r( py_type, initial_value, put_value, - use_dtype, use_proxy, ) for ( @@ -493,15 +483,9 @@ async def test_tango_signal_r( initial_value, put_value, ) in ATTRIBUTES_SET - for use_dtype in [True, False] - for use_proxy in [True, False] - ], - ids=[ - f"{x[0]}_{use_dtype}_{use_proxy}" - for x in ATTRIBUTES_SET - for use_dtype in [True, False] for use_proxy in [True, False] ], + ids=[f"{x[0]}_{use_proxy}" for x in ATTRIBUTES_SET for use_proxy in [True, False]], ) async def test_tango_signal_w( echo_device: str, @@ -511,13 +495,11 @@ async def test_tango_signal_w( py_type: Type[T], initial_value: T, put_value: T, - use_dtype: bool, use_proxy: bool, ): await prepare_device(echo_device, pv, initial_value) source = echo_device + "/" + pv proxy = await DeviceProxy(echo_device) if use_proxy else None - py_type = py_type if use_dtype else None timeout = 0.2 signal = tango_signal_w( @@ -548,7 +530,7 @@ async def test_tango_signal_w( # -------------------------------------------------------------------- @pytest.mark.asyncio @pytest.mark.parametrize( - "pv, tango_type, d_format, py_type, initial_value, put_value, use_dtype, use_proxy", + "pv, tango_type, d_format, py_type, initial_value, put_value, use_proxy", [ ( pv, @@ -557,7 +539,6 @@ async def test_tango_signal_w( py_type, initial_value, put_value, - use_dtype, use_proxy, ) for ( @@ -568,15 +549,9 @@ async def test_tango_signal_w( initial_value, put_value, ) in ATTRIBUTES_SET - for use_dtype in [True, False] - for use_proxy in [True, False] - ], - ids=[ - f"{x[0]}_{use_dtype}_{use_proxy}" - for x in ATTRIBUTES_SET - for use_dtype in [True, False] for use_proxy in [True, False] ], + ids=[f"{x[0]}_{use_proxy}" for x in ATTRIBUTES_SET for use_proxy in [True, False]], ) async def test_tango_signal_rw( echo_device: str, @@ -586,13 +561,11 @@ async def test_tango_signal_rw( py_type: Type[T], initial_value: T, put_value: T, - use_dtype: bool, use_proxy: bool, ): await prepare_device(echo_device, pv, initial_value) source = echo_device + "/" + pv proxy = await DeviceProxy(echo_device) if use_proxy else None - py_type = py_type if use_dtype else None timeout = 0.2 signal = tango_signal_rw( @@ -633,7 +606,7 @@ async def test_tango_signal_x(tango_test_device: str, use_proxy: bool): # -------------------------------------------------------------------- @pytest.mark.asyncio @pytest.mark.parametrize( - "pv, tango_type, d_format, py_type, initial_value, put_value, use_dtype, use_proxy", + "pv, tango_type, d_format, py_type, initial_value, put_value, use_proxy", [ ( pv, @@ -642,7 +615,6 @@ async def test_tango_signal_x(tango_test_device: str, use_proxy: bool): py_type, initial_value, put_value, - use_dtype, use_proxy, ) for ( @@ -653,15 +625,9 @@ async def test_tango_signal_x(tango_test_device: str, use_proxy: bool): initial_value, put_value, ) in ATTRIBUTES_SET - for use_dtype in [True, False] - for use_proxy in [True, False] - ], - ids=[ - f"{x[0]}_{use_dtype}_{use_proxy}" - for x in ATTRIBUTES_SET - for use_dtype in [True, False] for use_proxy in [True, False] ], + ids=[f"{x[0]}_{use_proxy}" for x in ATTRIBUTES_SET for use_proxy in [True, False]], ) async def test_tango_signal_auto_attrs( echo_device: str, @@ -671,7 +637,6 @@ async def test_tango_signal_auto_attrs( py_type: Type[T], initial_value: T, put_value: T, - use_dtype: bool, use_proxy: bool, ): await prepare_device(echo_device, pv, initial_value) @@ -680,7 +645,7 @@ async def test_tango_signal_auto_attrs( timeout = 0.2 async def _test_signal(dtype, proxy): - signal = tango_signal_auto( + signal = await tango_signal_auto( datatype=dtype, trl=source, device_proxy=proxy, @@ -702,7 +667,7 @@ async def _test_signal(dtype, proxy): value = value.tolist() assert_close(value, put_value) - dtype = py_type if use_dtype else None + dtype = py_type await _test_signal(dtype, proxy) @@ -754,7 +719,7 @@ async def test_tango_signal_auto_cmds( timeout = 0.2 async def _test_signal(dtype, proxy): - signal = tango_signal_auto( + signal = await tango_signal_auto( datatype=dtype, trl=source, device_proxy=proxy, @@ -762,7 +727,7 @@ async def _test_signal(dtype, proxy): timeout=timeout, ) # Ophyd SignalX does not support types - assert type(signal) is SignalRW + assert type(signal) in [SignalR, SignalRW, SignalW] await signal.connect() assert signal reading = await signal.read() @@ -785,7 +750,7 @@ async def _test_signal(dtype, proxy): @pytest.mark.parametrize("use_proxy", [True, False]) async def test_tango_signal_auto_cmds_void(tango_test_device: str, use_proxy: bool): proxy = await DeviceProxy(tango_test_device) if use_proxy else None - signal = tango_signal_auto( + signal = await tango_signal_auto( datatype=None, trl=tango_test_device + "/" + "clear", device_proxy=proxy, @@ -799,9 +764,11 @@ async def test_tango_signal_auto_cmds_void(tango_test_device: str, use_proxy: bo # -------------------------------------------------------------------- @pytest.mark.asyncio async def test_tango_signal_auto_badtrl(tango_test_device: str): + proxy = await DeviceProxy(tango_test_device) with pytest.raises(RuntimeError) as exc_info: - tango_signal_auto( + await tango_signal_auto( datatype=None, trl=tango_test_device + "/" + "badtrl", + device_proxy=proxy, ) assert f"Cannot find badtrl in {tango_test_device}" in str(exc_info.value)