diff --git a/docs/examples/epics_demo.py b/docs/examples/epics_demo.py index 0f0b1bfe6f..8e446ba2e5 100644 --- a/docs/examples/epics_demo.py +++ b/docs/examples/epics_demo.py @@ -7,7 +7,7 @@ from bluesky.utils import ProgressBarManager, register_transform from ophyd import Component, Device, EpicsSignal, EpicsSignalRO -from ophyd_async.core import DeviceCollector +from ophyd_async.core import init_devices from ophyd_async.epics import demo # Create a run engine, with plotting, progressbar and transform @@ -31,7 +31,7 @@ class OldSensor(Device): det_old = OldSensor(pv_prefix, name="det_old") # Create ophyd-async devices -with DeviceCollector(): +with init_devices(): det = demo.Sensor(pv_prefix) det_group = demo.SensorGroup(pv_prefix) samp = demo.SampleStage(pv_prefix) diff --git a/docs/explanations/event-loop-choice.rst b/docs/explanations/event-loop-choice.rst index 64b3ff58cb..4c33fb4e4d 100644 --- a/docs/explanations/event-loop-choice.rst +++ b/docs/explanations/event-loop-choice.rst @@ -1,7 +1,7 @@ Device Collector Event-Loop Choice ---------------------------------- -In a sync context, the ophyd-async :python:`DeviceCollector` requires the bluesky event-loop +In a sync context, the ophyd-async :python:`init_devices` requires the bluesky event-loop to connect to devices. In an async context, it does not. Sync Context @@ -14,25 +14,25 @@ The following will fail if :python:`RE = RunEngine()` has not been called alread .. code:: python - with DeviceCollector(): + with init_devices(): device1 = Device1(prefix) device2 = Device2(prefix) device3 = Device3(prefix) -The :python:`DeviceCollector` connects to devices in the event-loop created in the run-engine. +The :python:`init_devices` connects to devices in the event-loop created in the run-engine. Async Context ============= In an async context device connection is decoupled from the run-engine. -The following attempts connection to all the devices in the :python:`DeviceCollector` +The following attempts connection to all the devices in the :python:`init_devices` before or after run-engine initialization. .. code:: python async def connection_function() : - async with DeviceCollector(): + async with init_devices(): device1 = Device1(prefix) device2 = Device2(prefix) device3 = Device3(prefix) @@ -43,8 +43,8 @@ The devices will be unable to be used in the run-engine unless they share the sa When the run-engine is initialised it will create a new background event-loop to use if one is not passed in with :python:`RunEngine(loop=loop)`. -If the user wants to use devices in the async :python:`DeviceCollector` within the run-engine +If the user wants to use devices in the async :python:`init_devices` within the run-engine they can either: -* Run the :python:`DeviceCollector` first and pass the event-loop into the run-engine. -* Initialize the run-engine first and run the :python:`DeviceCollector` using the bluesky event-loop. +* Run the :python:`init_devices` first and pass the event-loop into the run-engine. +* Initialize the run-engine first and run the :python:`init_devices` using the bluesky event-loop. diff --git a/docs/how-to/write-tests-for-devices.rst b/docs/how-to/write-tests-for-devices.rst index 0ddd0cd462..f42cf3af8b 100644 --- a/docs/how-to/write-tests-for-devices.rst +++ b/docs/how-to/write-tests-for-devices.rst @@ -24,7 +24,7 @@ Async Tests Mock Backend ------------ -Ophyd devices initialized with a mock backend behave in a similar way to mocks, without requiring you to mock out all the dependencies and internals. The `DeviceCollector` can initialize any number of devices, and their signals and sub-devices (recursively), with a mock backend. +Ophyd devices initialized with a mock backend behave in a similar way to mocks, without requiring you to mock out all the dependencies and internals. The `init_devices` can initialize any number of devices, and their signals and sub-devices (recursively), with a mock backend. .. literalinclude:: ../../tests/epics/demo/test_demo.py :pyobject: mock_sensor diff --git a/docs/tutorials/using-existing-devices.rst b/docs/tutorials/using-existing-devices.rst index f9e2cc6c8d..d8ff51f4da 100644 --- a/docs/tutorials/using-existing-devices.rst +++ b/docs/tutorials/using-existing-devices.rst @@ -47,7 +47,7 @@ Finally we create the Ophyd Async devices imported from the `epics.demo` module: :language: python :start-after: # Create ophyd-async devices -The first thing to note is `with`. This uses a `DeviceCollector` as a context +The first thing to note is `with`. This uses `init_devices` as a context manager to collect up the top level `Device` instances created in the context, and run the following: diff --git a/src/ophyd_async/core/__init__.py b/src/ophyd_async/core/__init__.py index 3154572417..7da481c89e 100644 --- a/src/ophyd_async/core/__init__.py +++ b/src/ophyd_async/core/__init__.py @@ -5,7 +5,7 @@ StandardDetector, TriggerInfo, ) -from ._device import Device, DeviceCollector, DeviceConnector, DeviceVector +from ._device import Device, DeviceConnector, DeviceVector, init_devices from ._device_filler import DeviceFiller from ._flyer import FlyerController, StandardFlyer from ._hdf_dataset import HDFDataset, HDFFile @@ -87,7 +87,7 @@ "TriggerInfo", "Device", "DeviceConnector", - "DeviceCollector", + "init_devices", "DeviceVector", "DeviceFiller", "StandardFlyer", diff --git a/src/ophyd_async/core/_device.py b/src/ophyd_async/core/_device.py index 36bed049ca..54a70dc746 100644 --- a/src/ophyd_async/core/_device.py +++ b/src/ophyd_async/core/_device.py @@ -2,7 +2,7 @@ import asyncio import sys -from collections.abc import Coroutine, Iterator, Mapping, MutableMapping +from collections.abc import Awaitable, Callable, Iterator, Mapping, MutableMapping from functools import cached_property from logging import LoggerAdapter, getLogger from typing import Any, TypeVar @@ -254,54 +254,18 @@ def __hash__(self): # to allow DeviceVector to be used as dict keys and in sets return hash(id(self)) -class DeviceCollector: - """Collector of top level Device instances to be used as a context manager - - Parameters - ---------- - set_name: - If True, call ``device.set_name(variable_name)`` on all collected - Devices - child_name_separator: - Use this as a separator if we call ``set_name``. - connect: - If True, call ``device.connect(mock)`` in parallel on all - collected Devices - mock: - If True, connect Signals in simulation mode - timeout: - How long to wait for connect before logging an exception - - Notes - ----- - Example usage:: - - [async] with DeviceCollector(): - t1x = motor.Motor("BLxxI-MO-TABLE-01:X") - t1y = motor.Motor("pva://BLxxI-MO-TABLE-01:Y") - # Names and connects devices here - assert t1x.comm.velocity.source - assert t1x.name == "t1x" +class DeviceProcessor: + """Sync/Async Context Manager that finds all the Devices declared within it. + Used in `init_devices` """ - def __init__( - self, - set_name=True, - child_name_separator: str = "-", - connect=True, - mock=False, - timeout: float = 10.0, - ): - self._set_name = set_name - self._child_name_separator = child_name_separator - self._connect = connect - self._mock = mock - self._timeout = timeout - self._names_on_enter: set[str] = set() - self._objects_on_exit: dict[str, Any] = {} - - def _caller_locals(self): + def __init__(self, process_devices: Callable[[dict[str, Device]], Awaitable[None]]): + self._process_devices = process_devices + self._locals_on_enter: dict[str, Any] = {} + self._locals_on_exit: dict[str, Any] = {} + + def _caller_locals(self) -> dict[str, Any]: """Walk up until we find a stack frame that doesn't have us as self""" try: raise ValueError @@ -314,34 +278,18 @@ def _caller_locals(self): assert ( caller_frame ), "No previous frame to the one with self in it, this shouldn't happen" - return caller_frame.f_locals + return caller_frame.f_locals.copy() - def __enter__(self) -> DeviceCollector: + def __enter__(self) -> DeviceProcessor: # Stash the names that were defined before we were called - self._names_on_enter = set(self._caller_locals()) + self._locals_on_enter = self._caller_locals() return self - async def __aenter__(self) -> DeviceCollector: + async def __aenter__(self) -> DeviceProcessor: return self.__enter__() - async def _on_exit(self) -> None: - # Name and kick off connect for devices - connect_coroutines: dict[str, Coroutine] = {} - for name, obj in self._objects_on_exit.items(): - if name not in self._names_on_enter and isinstance(obj, Device): - if self._set_name and not obj.name: - obj.set_name(name, child_name_separator=self._child_name_separator) - if self._connect: - connect_coroutines[name] = obj.connect( - self._mock, timeout=self._timeout - ) - - # Connect to all the devices - if connect_coroutines: - await wait_for_connection(**connect_coroutines) - async def __aexit__(self, type, value, traceback): - self._objects_on_exit = self._caller_locals() + self._locals_on_exit = self._caller_locals() await self._on_exit() def __exit__(self, type_, value, traceback): @@ -350,7 +298,7 @@ def __exit__(self, type_, value, traceback): "Cannot use DeviceConnector inside a plan, instead use " "`yield from ophyd_async.plan_stubs.ensure_connected(device)`" ) - self._objects_on_exit = self._caller_locals() + self._locals_on_exit = self._caller_locals() try: fut = call_in_bluesky_event_loop(self._on_exit()) except RuntimeError as e: @@ -360,3 +308,62 @@ def __exit__(self, type_, value, traceback): "user/explanations/event-loop-choice.html for more info." ) from e return fut + + async def _on_exit(self) -> None: + # Find all the devices + devices = { + name: obj + for name, obj in self._locals_on_exit.items() + if isinstance(obj, Device) and self._locals_on_enter.get(name) is not obj + } + # Call the provided process function on them + await self._process_devices(devices) + + +def init_devices( + set_name=True, + child_name_separator: str = "-", + connect=True, + mock=False, + timeout: float = 10.0, +) -> DeviceProcessor: + """Auto initialise top level Device instances to be used as a context manager + + Parameters + ---------- + set_name: + If True, call ``device.set_name(variable_name)`` on all Devices + created within the context manager that have an empty ``name`` + child_name_separator: + Use this as a separator if we call ``set_name``. + connect: + If True, call ``device.connect(mock, timeout)`` in parallel on all + Devices created within the context manager + mock: + If True, connect Signals in mock mode + timeout: + How long to wait for connect before logging an exception + + Notes + ----- + Example usage:: + + [async] with init_devices(): + t1x = motor.Motor("BLxxI-MO-TABLE-01:X") + t1y = motor.Motor("pva://BLxxI-MO-TABLE-01:Y") + # Names and connects devices here + assert t1x.name == "t1x" + """ + + async def process_devices(devices: dict[str, Device]): + if set_name: + for name, device in devices.items(): + if not device.name: + device.set_name(name, child_name_separator=child_name_separator) + if connect: + coros = { + name: device.connect(mock, timeout) for name, device in devices.items() + } + await wait_for_connection(**coros) + + return DeviceProcessor(process_devices) diff --git a/system_tests/epics/eiger/test_eiger_system.py b/system_tests/epics/eiger/test_eiger_system.py index 8a27c41689..ea377f911a 100644 --- a/system_tests/epics/eiger/test_eiger_system.py +++ b/system_tests/epics/eiger/test_eiger_system.py @@ -9,8 +9,8 @@ from ophyd_async.core import ( DetectorTrigger, Device, - DeviceCollector, StaticPathProvider, + init_devices, ) from ophyd_async.epics.core import epics_signal_rw from ophyd_async.epics.eiger import EigerDetector, EigerTriggerInfo @@ -47,7 +47,7 @@ def RE(): @pytest.fixture async def setup_device(RE, ioc_prefixes): - async with DeviceCollector(): + async with init_devices(): device = SetupDevice(ioc_prefixes[0], ioc_prefixes[1] + "FP:") await asyncio.gather( device.header_detail.set("all"), @@ -62,7 +62,7 @@ async def setup_device(RE, ioc_prefixes): @pytest.fixture async def test_eiger(RE, ioc_prefixes) -> EigerDetector: provider = StaticPathProvider(lambda: "test_eiger", Path(SAVE_PATH)) - async with DeviceCollector(): + async with init_devices(): test_eiger = EigerDetector("", provider, ioc_prefixes[0], ioc_prefixes[1]) return test_eiger diff --git a/tests/core/test_device_collector.py b/tests/core/test_auto_init_devices.py similarity index 75% rename from tests/core/test_device_collector.py rename to tests/core/test_auto_init_devices.py index 18c9b5deb7..ef0338706b 100644 --- a/tests/core/test_device_collector.py +++ b/tests/core/test_auto_init_devices.py @@ -7,8 +7,8 @@ from ophyd_async.core import ( DEFAULT_TIMEOUT, Device, - DeviceCollector, NotConnected, + init_devices, ) from ophyd_async.epics import motor from ophyd_async.testing import set_mock_value @@ -33,10 +33,10 @@ async def connect( async def set(self, new_position: float): ... -async def test_device_collector_handles_top_level_errors(caplog): +async def test_init_devices_handles_top_level_errors(caplog): caplog.set_level(10) with pytest.raises(NotConnected) as exc: - async with DeviceCollector(): + async with init_devices(): _ = FailingDevice("somename") assert not exc.value.__cause__ @@ -52,9 +52,9 @@ async def test_device_collector_handles_top_level_errors(caplog): assert device_log[0].levelname == "ERROR" -def test_sync_device_connector_no_run_engine_raises_error(): +def test_sync_init_devices_no_run_engine_raises_error(): with pytest.raises(NotConnected) as e: - with DeviceCollector(): + with init_devices(): working_device = WorkingDevice("somename") assert e.value._errors == ( "Could not connect devices. Is the bluesky event loop running? See " @@ -64,26 +64,36 @@ def test_sync_device_connector_no_run_engine_raises_error(): assert not working_device.connected -def test_sync_device_connector_run_engine_created_connects(RE): - with DeviceCollector(): +def test_sync_init_devices_run_engine_created_connects(RE): + with init_devices(): working_device = WorkingDevice("somename") assert working_device.connected +async def test_init_devices_detects_redeclared_devices(): + original_working_device = working_device = WorkingDevice() + + async with init_devices(): + working_device = WorkingDevice() + assert original_working_device is not working_device + assert working_device.connected and working_device.name == "working_device" + assert not original_working_device.connected and original_working_device.name == "" + + def test_connecting_in_plan_raises(RE): def bad_plan(): yield from bps.null() - with DeviceCollector(): + with init_devices(): working_device = WorkingDevice("somename") # noqa: F841 with pytest.raises(RuntimeError, match="Cannot use DeviceConnector inside a plan"): RE(bad_plan()) -def test_async_device_connector_run_engine_same_event_loop(): +def test_async_init_devices_run_engine_same_event_loop(): async def set_up_device(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_motor = motor.Motor("BLxxI-MO-TABLE-01:X") set_mock_value(mock_motor.velocity, 1) return mock_motor @@ -126,17 +136,17 @@ def my_plan(): "loop to set the value, unlike real signals." ) ) -def test_async_device_connector_run_engine_different_event_loop(): +def test_async_init_devices_run_engine_different_event_loop(): async def set_up_device(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_motor = motor.Motor("BLxxI-MO-TABLE-01:X") return mock_motor - device_connector_loop = asyncio.new_event_loop() + init_devices_loop = asyncio.new_event_loop() run_engine_loop = asyncio.new_event_loop() - assert run_engine_loop is not device_connector_loop + assert run_engine_loop is not init_devices_loop - mock_motor = device_connector_loop.run_until_complete(set_up_device()) + mock_motor = init_devices_loop.run_until_complete(set_up_device()) RE = RunEngine(loop=run_engine_loop) @@ -147,7 +157,7 @@ def my_plan(): # The set should fail since the run engine is on a different event loop assert ( - device_connector_loop.run_until_complete(mock_motor.user_setpoint.read())[ + init_devices_loop.run_until_complete(mock_motor.user_setpoint.read())[ "mock_motor-user_setpoint" ]["value"] != 3.14 diff --git a/tests/core/test_device.py b/tests/core/test_device.py index 9850da8eee..ef87641b30 100644 --- a/tests/core/test_device.py +++ b/tests/core/test_device.py @@ -8,11 +8,11 @@ from ophyd_async.core import ( DEFAULT_TIMEOUT, Device, - DeviceCollector, DeviceVector, NotConnected, Reference, SignalRW, + init_devices, soft_signal_rw, wait_for_connection, ) @@ -126,8 +126,8 @@ async def test_children_of_device_with_different_separator( assert parent.dict_with_children[123].name == "parent_dict_with_children_123" -async def test_device_with_device_collector(): - async with DeviceCollector(mock=True): +async def test_device_with_init_devices(): + async with init_devices(mock=True): parent = DummyDeviceGroup("parent") assert parent.name == "parent" diff --git a/tests/core/test_mock_signal_backend.py b/tests/core/test_mock_signal_backend.py index 27dd70b190..8370e7340f 100644 --- a/tests/core/test_mock_signal_backend.py +++ b/tests/core/test_mock_signal_backend.py @@ -6,11 +6,11 @@ from ophyd_async.core import ( Device, - DeviceCollector, MockSignalBackend, SignalRW, SignalW, SoftSignalBackend, + init_devices, soft_signal_r_and_setter, soft_signal_rw, ) @@ -170,7 +170,7 @@ def err_text(text, wait): @pytest.fixture async def mock_signals(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): signal1 = epics_signal_rw(str, "READ_PV1", "WRITE_PV1", name="mock_name1") signal2 = epics_signal_rw(str, "READ_PV2", "WRITE_PV2", name="mock_name2") diff --git a/tests/core/test_protocol.py b/tests/core/test_protocol.py index 637d287213..5d584b2dd1 100644 --- a/tests/core/test_protocol.py +++ b/tests/core/test_protocol.py @@ -4,10 +4,10 @@ from ophyd_async.core import ( AsyncReadable, - DeviceCollector, StandardFlyer, StaticFilenameProvider, StaticPathProvider, + init_devices, ) from ophyd_async.epics import adsimdetector from ophyd_async.sim.demo import SimMotor @@ -17,7 +17,7 @@ async def make_detector(prefix: str, name: str, tmp_path: Path): fp = StaticFilenameProvider(f"test-{new_uid()}") dp = StaticPathProvider(fp, tmp_path) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): det = adsimdetector.SimDetector(prefix, dp, name=name) det._config_sigs = [det.drv.acquire_time, det.drv.acquire] @@ -26,7 +26,7 @@ async def make_detector(prefix: str, name: str, tmp_path: Path): async def test_readable(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): det = await make_detector("test", "test det", Path("/tmp")) assert isinstance(SimMotor, AsyncReadable) assert isinstance(det, AsyncReadable) diff --git a/tests/core/test_signal.py b/tests/core/test_signal.py index c88597ec12..9009554920 100644 --- a/tests/core/test_signal.py +++ b/tests/core/test_signal.py @@ -12,12 +12,12 @@ from ophyd_async.core import ( Array1D, - DeviceCollector, SignalR, SignalRW, SoftSignalBackend, StandardReadable, StrictEnum, + init_devices, set_and_wait_for_other_value, set_and_wait_for_value, soft_signal_r_and_setter, @@ -275,7 +275,7 @@ def __init__(self, prefix: str, name="") -> None: @pytest.fixture async def mock_readable(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_readable = DummyReadableArray("SIM:READABLE:", name="mock_readable") yield mock_readable diff --git a/tests/core/test_utils.py b/tests/core/test_utils.py index 2dea901a27..7a1f07a2a9 100644 --- a/tests/core/test_utils.py +++ b/tests/core/test_utils.py @@ -5,7 +5,7 @@ from ophyd_async.core import ( DEFAULT_TIMEOUT, Device, - DeviceCollector, + init_devices, SoftSignalBackend, NotConnected, SignalRW, @@ -179,9 +179,9 @@ def __init__(self, name: str = "") -> None: super().__init__(name) -async def test_error_handling_device_collector_mock(): +async def test_error_handling_init_devices_mock(): with pytest.raises(NotConnected) as e: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): device = BadDatatypeDevice() device2 = BadDatatypeDevice() expected_output = NotConnected( @@ -205,11 +205,11 @@ def test_introspecting_sub_errors(): assert error.sub_errors == {"child1": sub_error1, "child2": sub_error2} -async def test_error_handling_device_collector(caplog): +async def test_error_handling_init_devices(caplog): caplog.set_level(10) with pytest.raises(NotConnected) as e: # flake8: noqa - async with DeviceCollector(timeout=0.1): + async with init_devices(timeout=0.1): dummy_device_two_working_one_timeout_two_value_error = ( DummyDeviceTwoWorkingTwoTimeOutTwoValueError() ) @@ -271,7 +271,7 @@ async def test_combining_top_level_signal_and_child_device(): ) with pytest.raises(NotConnected) as e: - async with DeviceCollector(timeout=0.1): + async with init_devices(timeout=0.1): dummy_device2 = DummyDeviceCombiningTopLevelSignalAndSubDevice() assert str(e.value) == ( "\ndummy_device2: NotConnected:\n" diff --git a/tests/epics/adcore/test_drivers.py b/tests/epics/adcore/test_drivers.py index 09c72836cf..92848a7174 100644 --- a/tests/epics/adcore/test_drivers.py +++ b/tests/epics/adcore/test_drivers.py @@ -5,7 +5,7 @@ from ophyd_async.core import ( DetectorController, - DeviceCollector, + init_devices, ) from ophyd_async.epics import adcore from ophyd_async.testing import get_mock_put, set_mock_value @@ -15,7 +15,7 @@ @pytest.fixture def driver(RE) -> adcore.ADBaseIO: - with DeviceCollector(mock=True): + with init_devices(mock=True): driver = adcore.ADBaseIO("DRV:", name="drv") return driver diff --git a/tests/epics/adcore/test_scans.py b/tests/epics/adcore/test_scans.py index bc70de97d6..39d584dff1 100644 --- a/tests/epics/adcore/test_scans.py +++ b/tests/epics/adcore/test_scans.py @@ -12,11 +12,11 @@ AsyncStatus, DetectorController, DetectorTrigger, - DeviceCollector, FlyerController, StandardDetector, StandardFlyer, TriggerInfo, + init_devices, ) from ophyd_async.epics import adcore, adsimdetector from ophyd_async.testing import set_mock_value @@ -54,7 +54,7 @@ def get_deadtime(self, exposure: float | None) -> float: @pytest.fixture def controller(RE) -> adsimdetector.SimController: - with DeviceCollector(mock=True): + with init_devices(mock=True): drv = adcore.ADBaseIO("DRV") return adsimdetector.SimController(drv) @@ -62,7 +62,7 @@ def controller(RE) -> adsimdetector.SimController: @pytest.fixture def writer(RE, static_path_provider, tmp_path: Path) -> adcore.ADHDFWriter: - with DeviceCollector(mock=True): + with init_devices(mock=True): hdf = adcore.NDFileHDFIO("HDF") return adcore.ADHDFWriter( diff --git a/tests/epics/adcore/test_writers.py b/tests/epics/adcore/test_writers.py index d7feff7e8f..ab6f4f1f9d 100644 --- a/tests/epics/adcore/test_writers.py +++ b/tests/epics/adcore/test_writers.py @@ -5,10 +5,10 @@ from ophyd_async.core import ( DatasetDescriber, - DeviceCollector, PathProvider, StandardDetector, StaticPathProvider, + init_devices, ) from ophyd_async.epics import adaravis, adcore, adkinetix, adpilatus, advimba from ophyd_async.epics.core import epics_signal_r @@ -28,7 +28,7 @@ async def shape(self) -> tuple[int, int]: async def hdf_writer( RE, static_path_provider: StaticPathProvider ) -> adcore.ADHDFWriter: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): hdf = adcore.NDFileHDFIO("HDF:") return adcore.ADHDFWriter( @@ -43,7 +43,7 @@ async def hdf_writer( async def hdf_writer_with_stats( RE, static_path_provider: StaticPathProvider ) -> adcore.ADHDFWriter: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): hdf = adcore.NDFileHDFIO("HDF:") stats = adcore.NDPluginStatsIO("FOO:") @@ -64,7 +64,7 @@ async def detectors( static_path_provider: PathProvider, ) -> list[StandardDetector]: detectors = [] - async with DeviceCollector(mock=True): + async with init_devices(mock=True): detectors.append(advimba.VimbaDetector("VIMBA:", static_path_provider)) detectors.append(adkinetix.KinetixDetector("KINETIX:", static_path_provider)) detectors.append(adpilatus.PilatusDetector("PILATUS:", static_path_provider)) diff --git a/tests/epics/conftest.py b/tests/epics/conftest.py index e563207109..6900791d66 100644 --- a/tests/epics/conftest.py +++ b/tests/epics/conftest.py @@ -5,8 +5,8 @@ from bluesky.run_engine import RunEngine from ophyd_async.core import ( - DeviceCollector, StandardDetector, + init_devices, ) from ophyd_async.testing import callback_on_mock_put, set_mock_value @@ -24,7 +24,7 @@ def generate_ad_standard_det( if detector_name.endswith("Detector"): detector_name = detector_name[: -len("Detector")] - with DeviceCollector(mock=True): + with init_devices(mock=True): test_adstandard_det = ad_standard_detector_class( f"{detector_name.upper()}{number}:", static_path_provider, diff --git a/tests/epics/demo/test_demo.py b/tests/epics/demo/test_demo.py index eaa6c24cdd..2fc6d3d808 100644 --- a/tests/epics/demo/test_demo.py +++ b/tests/epics/demo/test_demo.py @@ -9,9 +9,9 @@ from bluesky.run_engine import RunEngine from ophyd_async.core import ( - DeviceCollector, LazyMock, NotConnected, + init_devices, ) from ophyd_async.epics import demo from ophyd_async.testing import ( @@ -28,7 +28,7 @@ @pytest.fixture async def mock_mover() -> demo.Mover: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_mover = demo.Mover("BLxxI-MO-TABLE-01:X:") # Signals connected here @@ -41,7 +41,7 @@ async def mock_mover() -> demo.Mover: @pytest.fixture async def mock_sensor() -> demo.Sensor: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_sensor = demo.Sensor("MOCK:SENSOR:") # Signals connected here @@ -51,7 +51,7 @@ async def mock_sensor() -> demo.Sensor: @pytest.fixture async def mock_sensor_group() -> demo.SensorGroup: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_sensor_group = demo.SensorGroup("MOCK:SENSOR:") # Signals connected here @@ -252,7 +252,7 @@ async def test_set_velocity(mock_mover: demo.Mover) -> None: async def test_mover_disconnected(): with pytest.raises(NotConnected): - async with DeviceCollector(timeout=0.1): + async with init_devices(timeout=0.1): m = demo.Mover("ca://PRE:", name="mover") assert m.name == "mover" @@ -260,7 +260,7 @@ async def test_mover_disconnected(): async def test_sensor_disconnected(caplog): caplog.set_level(10) with pytest.raises(NotConnected): - async with DeviceCollector(timeout=0.1): + async with init_devices(timeout=0.1): s = demo.Sensor("ca://PRE:", name="sensor") logs = caplog.get_records("call") logs = [log for log in logs if "_signal" not in log.pathname] @@ -317,7 +317,7 @@ async def test_assembly_renaming() -> None: async def test_dynamic_sensor_group_disconnected(): with pytest.raises(NotConnected) as e: - async with DeviceCollector(timeout=0.1): + async with init_devices(timeout=0.1): mock_sensor_group_dynamic = demo.SensorGroup("MOCK:SENSOR:") expected = """ mock_sensor_group_dynamic: NotConnected: diff --git a/tests/epics/eiger/test_eiger_controller.py b/tests/epics/eiger/test_eiger_controller.py index 764f49028b..c3e368d07c 100644 --- a/tests/epics/eiger/test_eiger_controller.py +++ b/tests/epics/eiger/test_eiger_controller.py @@ -3,8 +3,8 @@ from pytest import fixture, raises from ophyd_async.core import ( - DeviceCollector, TriggerInfo, + init_devices, ) from ophyd_async.epics.eiger import EigerController, EigerDriverIO from ophyd_async.testing import callback_on_mock_put, get_mock_put, set_mock_value @@ -14,7 +14,7 @@ @fixture def eiger_driver_and_controller_no_arm(RE) -> DriverAndController: - with DeviceCollector(mock=True): + with init_devices(mock=True): driver = EigerDriverIO("") controller = EigerController(driver) diff --git a/tests/epics/eiger/test_eiger_detector.py b/tests/epics/eiger/test_eiger_detector.py index 300d56ff04..74115f8e79 100644 --- a/tests/epics/eiger/test_eiger_detector.py +++ b/tests/epics/eiger/test_eiger_detector.py @@ -2,14 +2,14 @@ import pytest -from ophyd_async.core import DetectorTrigger, DeviceCollector +from ophyd_async.core import DetectorTrigger, init_devices from ophyd_async.epics.eiger import EigerDetector, EigerTriggerInfo from ophyd_async.testing import get_mock_put @pytest.fixture def detector(RE): - with DeviceCollector(mock=True): + with init_devices(mock=True): detector = EigerDetector("BL03I", MagicMock()) return detector diff --git a/tests/epics/eiger/test_odin_io.py b/tests/epics/eiger/test_odin_io.py index 4c369a80a9..5499fc4860 100644 --- a/tests/epics/eiger/test_odin_io.py +++ b/tests/epics/eiger/test_odin_io.py @@ -3,7 +3,7 @@ import pytest -from ophyd_async.core import DeviceCollector +from ophyd_async.core import init_devices from ophyd_async.epics.eiger._odin_io import Odin, OdinWriter, Writing # noqa: PLC2701 from ophyd_async.testing import get_mock_put, set_mock_value @@ -12,7 +12,7 @@ @pytest.fixture def odin_driver_and_writer(RE) -> OdinDriverAndWriter: - with DeviceCollector(mock=True): + with init_devices(mock=True): driver = Odin("") writer = OdinWriter(MagicMock(), lambda: "odin", driver) return driver, writer diff --git a/tests/epics/pvi/test_pvi.py b/tests/epics/pvi/test_pvi.py index 6391f98121..6da9392247 100644 --- a/tests/epics/pvi/test_pvi.py +++ b/tests/epics/pvi/test_pvi.py @@ -6,11 +6,11 @@ from ophyd_async.core import ( Device, - DeviceCollector, DeviceVector, SignalRW, SignalX, StandardReadable, + init_devices, ) from ophyd_async.core import StandardReadableFormat as Format from ophyd_async.epics.core import PviDeviceConnector @@ -62,7 +62,7 @@ def with_pvi_connector( async def test_fill_pvi_entries_mock_mode(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): test_device = with_pvi_connector(Block3, "PREFIX:") # device vectors are typed diff --git a/tests/epics/test_motor.py b/tests/epics/test_motor.py index aabb120ac6..bc08b5f52b 100644 --- a/tests/epics/test_motor.py +++ b/tests/epics/test_motor.py @@ -8,7 +8,7 @@ from ophyd_async.core import ( CALCULATE_TIMEOUT, AsyncStatus, - DeviceCollector, + init_devices, observe_value, soft_signal_rw, ) @@ -25,7 +25,7 @@ @pytest.fixture async def sim_motor(): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): sim_motor = motor.Motor("BLxxI-MO-TABLE-01:X", name="sim_motor") set_mock_value(sim_motor.motor_egu, "mm") diff --git a/tests/fastcs/panda/test_panda_connect.py b/tests/fastcs/panda/test_panda_connect.py index e395ef1ca4..765fe19bb3 100644 --- a/tests/fastcs/panda/test_panda_connect.py +++ b/tests/fastcs/panda/test_panda_connect.py @@ -9,9 +9,9 @@ from ophyd_async.core import ( Device, - DeviceCollector, DeviceVector, NotConnected, + init_devices, ) from ophyd_async.fastcs.core import fastcs_connector from ophyd_async.fastcs.panda import ( @@ -65,7 +65,7 @@ def __init__(self, uri: str, name: str = ""): @pytest.fixture async def mock_panda(panda_t): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_panda = panda_t("PANDAQSRV:") assert mock_panda.name == "mock_panda" diff --git a/tests/fastcs/panda/test_panda_control.py b/tests/fastcs/panda/test_panda_control.py index 867d97330a..defb6f52be 100644 --- a/tests/fastcs/panda/test_panda_control.py +++ b/tests/fastcs/panda/test_panda_control.py @@ -4,7 +4,7 @@ import pytest -from ophyd_async.core import DetectorTrigger, Device, DeviceCollector, TriggerInfo +from ophyd_async.core import DetectorTrigger, Device, TriggerInfo, init_devices from ophyd_async.epics.core import epics_signal_rw from ophyd_async.fastcs.core import fastcs_connector from ophyd_async.fastcs.panda import CommonPandaBlocks, PandaPcapController @@ -16,7 +16,7 @@ class Panda(CommonPandaBlocks): def __init__(self, uri: str, name: str = ""): super().__init__(name=name, connector=fastcs_connector(self, uri)) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_panda = Panda("PANDACONTROLLER:", name="mock_panda") mock_panda.phase_1_signal_units = epics_signal_rw(int, "") yield mock_panda diff --git a/tests/fastcs/panda/test_panda_utils.py b/tests/fastcs/panda/test_panda_utils.py index 9a0855ad33..13aa4704f2 100644 --- a/tests/fastcs/panda/test_panda_utils.py +++ b/tests/fastcs/panda/test_panda_utils.py @@ -2,7 +2,7 @@ import yaml from bluesky import RunEngine -from ophyd_async.core import DeviceCollector, YamlSettingsProvider +from ophyd_async.core import YamlSettingsProvider, init_devices from ophyd_async.epics.core import epics_signal_rw from ophyd_async.fastcs.core import fastcs_connector from ophyd_async.fastcs.panda import CommonPandaBlocks, DataBlock, SeqTable, TimeUnits @@ -20,7 +20,7 @@ class Panda(CommonPandaBlocks): def __init__(self, uri: str, name: str = ""): super().__init__(name=name, connector=fastcs_connector(self, uri)) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_panda = Panda("PANDA") mock_panda.phase_1_signal_units = epics_signal_rw(int, "") return mock_panda diff --git a/tests/fastcs/panda/test_trigger.py b/tests/fastcs/panda/test_trigger.py index fbe68a4229..7c80d3063b 100644 --- a/tests/fastcs/panda/test_trigger.py +++ b/tests/fastcs/panda/test_trigger.py @@ -4,7 +4,7 @@ import pytest from pydantic import ValidationError -from ophyd_async.core import DeviceCollector +from ophyd_async.core import init_devices from ophyd_async.fastcs.core import fastcs_connector from ophyd_async.fastcs.panda import ( CommonPandaBlocks, @@ -25,7 +25,7 @@ class Panda(CommonPandaBlocks): def __init__(self, uri: str, name: str = ""): super().__init__(name=name, connector=fastcs_connector(self, uri)) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_panda = Panda("PANDAQSRV:", "mock_panda") assert mock_panda.name == "mock_panda" diff --git a/tests/fastcs/panda/test_writer.py b/tests/fastcs/panda/test_writer.py index 3449408ae8..f184ceaf07 100644 --- a/tests/fastcs/panda/test_writer.py +++ b/tests/fastcs/panda/test_writer.py @@ -8,11 +8,11 @@ from ophyd_async.core import ( Device, - DeviceCollector, HDFFile, SignalR, StaticFilenameProvider, StaticPathProvider, + init_devices, ) from ophyd_async.fastcs.core import fastcs_connector from ophyd_async.fastcs.panda import ( @@ -66,7 +66,7 @@ def __init__(self, uri: str, name: str = ""): @pytest.fixture async def mock_panda(panda_t): - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_panda = panda_t("mock_PANDA", name="mock_panda") # Mimic directory exists check that happens normally in the PandA IOC @@ -92,7 +92,7 @@ def check_dir_exits(value, **kwargs): async def mock_writer(tmp_path, mock_panda) -> PandaHDFWriter: fp = StaticFilenameProvider("data") dp = StaticPathProvider(fp, tmp_path / mock_panda.name, create_dir_depth=-1) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): writer = PandaHDFWriter( path_provider=dp, name_provider=lambda: mock_panda.name, @@ -215,7 +215,7 @@ async def test_oserror_when_hdf_dir_does_not_exist(tmp_path, mock_panda): dp = StaticPathProvider( fp, tmp_path / mock_panda.name / "extra" / "dirs", create_dir_depth=-1 ) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): writer = PandaHDFWriter( path_provider=dp, name_provider=lambda: "test-panda", diff --git a/tests/plan_stubs/test_fly.py b/tests/plan_stubs/test_fly.py index 6f74815316..aac15bf7ea 100644 --- a/tests/plan_stubs/test_fly.py +++ b/tests/plan_stubs/test_fly.py @@ -14,13 +14,13 @@ AsyncStatus, DetectorController, DetectorWriter, - DeviceCollector, FlyerController, SignalR, StandardDetector, StandardFlyer, WatchableAsyncStatus, WatcherUpdate, + init_devices, observe_value, ) from ophyd_async.epics.core import epics_signal_rw @@ -173,7 +173,7 @@ class Panda(CommonPandaBlocks): def __init__(self, uri: str, name: str = ""): super().__init__(name=name, connector=fastcs_connector(self, uri)) - async with DeviceCollector(mock=True): + async with init_devices(mock=True): mock_panda = Panda("PANDAQSRV:", "mock_panda") assert mock_panda.name == "mock_panda" diff --git a/tests/sim/demo/test_sim_motor.py b/tests/sim/demo/test_sim_motor.py index e88dbee04c..0106c48cdb 100644 --- a/tests/sim/demo/test_sim_motor.py +++ b/tests/sim/demo/test_sim_motor.py @@ -5,14 +5,14 @@ from bluesky.plans import spiral_square from bluesky.run_engine import RunEngine -from ophyd_async.core import DeviceCollector +from ophyd_async.core import init_devices from ophyd_async.sim.demo import SimMotor async def test_move_sim_in_plan(): RE = RunEngine() - async with DeviceCollector(): + async with init_devices(): m1 = SimMotor("M1") m2 = SimMotor("M2") @@ -25,7 +25,7 @@ async def test_move_sim_in_plan(): async def test_slow_move(): - async with DeviceCollector(): + async with init_devices(): m1 = SimMotor("M1", instant=False) await m1.velocity.set(20) @@ -77,7 +77,7 @@ async def test_negative_move(): async def test_stop(): - async with DeviceCollector(): + async with init_devices(): m1 = SimMotor("M1", instant=False) # this move should take 10 seconds but we will stop it after 0.5 diff --git a/tests/sim/test_sim_writer.py b/tests/sim/test_sim_writer.py index 5ee2bba97a..fd0a1e6a77 100644 --- a/tests/sim/test_sim_writer.py +++ b/tests/sim/test_sim_writer.py @@ -2,13 +2,13 @@ import pytest -from ophyd_async.core import DeviceCollector +from ophyd_async.core import init_devices from ophyd_async.sim.demo import PatternDetectorWriter, PatternGenerator @pytest.fixture async def writer(static_path_provider) -> PatternDetectorWriter: - async with DeviceCollector(mock=True): + async with init_devices(mock=True): driver = PatternGenerator() return PatternDetectorWriter(driver, static_path_provider, lambda: "NAME") diff --git a/tests/tango/test_base_device.py b/tests/tango/test_base_device.py index 60c6db9762..365fe7ee2b 100644 --- a/tests/tango/test_base_device.py +++ b/tests/tango/test_base_device.py @@ -10,7 +10,7 @@ from bluesky import RunEngine import tango -from ophyd_async.core import Array1D, DeviceCollector, SignalRW, T +from ophyd_async.core import Array1D, SignalRW, T, init_devices from ophyd_async.core import StandardReadableFormat as Format from ophyd_async.tango.core import TangoReadable, get_python_type from ophyd_async.tango.demo import ( @@ -302,7 +302,7 @@ def compare_values(expected, received): async def test_connect(tango_test_device): values, description = await describe_class(tango_test_device) - async with DeviceCollector(): + async with init_devices(): test_device = TestTangoReadable(tango_test_device) assert test_device.name == "test_device" @@ -349,7 +349,7 @@ async def test_connect_proxy(tango_test_device, proxy: bool | None): async def test_with_bluesky(tango_test_device): # now let's do some bluesky stuff RE = RunEngine() - with DeviceCollector(): + with init_devices(): device = TestTangoReadable(tango_test_device) RE(bp.count([device]))