From 14ab6cb41338845c6f275391eeabb1078829e84c Mon Sep 17 00:00:00 2001 From: Dominic Oram Date: Tue, 18 Nov 2025 12:28:30 +0000 Subject: [PATCH 1/4] Fix tests for new device manager --- tests/conftest.py | 83 +++++++++---------- tests/system_tests/conftest.py | 2 +- .../hyperion/external_interaction/conftest.py | 2 +- .../inner_plans/test_read_hardware.py | 8 +- .../test_common_flyscan_xray_centre_plan.py | 2 +- .../test_grid_detection_plan.py | 4 +- tests/unit_tests/conftest.py | 6 +- .../hyperion/device_setup_plans/test_utils.py | 2 +- .../test_optimise_attenuation_plan.py | 8 +- .../test_robot_load_then_centre.py | 5 ++ 10 files changed, 64 insertions(+), 58 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 31a748fe30..c616b02666 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -398,7 +398,7 @@ def done_status(): @pytest.fixture def eiger(done_status): - eiger = i03.eiger(connect_immediately=True, mock=True) + eiger = i03.eiger.build(mock=True) eiger.stage = MagicMock(return_value=done_status) eiger.do_arm.set = MagicMock(return_value=done_status) eiger.unstage = MagicMock(return_value=done_status) @@ -407,7 +407,7 @@ def eiger(done_status): @pytest.fixture def smargon() -> Generator[Smargon, None, None]: - smargon = i03.smargon(connect_immediately=True, mock=True) + smargon = i03.smargon.build(connect_immediately=True, mock=True) # Initial positions, needed for stub_offsets set_mock_value(smargon.stub_offsets.center_at_current_position.disp, 0) @@ -427,7 +427,7 @@ def aithre_gonio(): @pytest.fixture def zebra(): - zebra = i03.zebra(connect_immediately=True, mock=True) + zebra = i03.zebra.build(connect_immediately=True, mock=True) def mock_side(demand: ArmDemand): set_mock_value(zebra.pc.arm.armed, demand.value) @@ -439,19 +439,19 @@ def mock_side(demand: ArmDemand): @pytest.fixture def zebra_shutter(): - return i03.sample_shutter(connect_immediately=True, mock=True) + return i03.sample_shutter.build(connect_immediately=True, mock=True) @pytest.fixture def backlight(): - backlight = i03.backlight(connect_immediately=True, mock=True) + backlight = i03.backlight.build(connect_immediately=True, mock=True) backlight.TIME_TO_MOVE_S = 0.001 return backlight @pytest.fixture def baton(): - baton = i03.baton(connect_immediately=True, mock=True) + baton = i03.baton.build(connect_immediately=True, mock=True) set_mock_value(baton.requested_user, HYPERION_USER) set_mock_value(baton.current_user, HYPERION_USER) return baton @@ -467,7 +467,7 @@ def baton_in_commissioning_mode(baton: Baton): @pytest.fixture def fast_grid_scan(): - scan = i03.zebra_fast_grid_scan(connect_immediately=True, mock=True) + scan = i03.zebra_fast_grid_scan.build(mock=True, connect_immediately=True) for signal in [scan.x_scan_valid, scan.y_scan_valid, scan.z_scan_valid]: set_mock_value(signal, 1) return scan @@ -475,26 +475,26 @@ def fast_grid_scan(): @pytest.fixture def detector_motion(): - det = i03.detector_motion(connect_immediately=True, mock=True) + det = i03.detector_motion.build(mock=True, connect_immediately=True) with patch_all_motors(det): yield det @pytest.fixture -def undulator(baton): - undulator = i03.undulator(connect_immediately=True, mock=True) +def undulator(): + undulator = i03.undulator.build(mock=True, connect_immediately=True) with patch_all_motors(undulator): yield undulator @pytest.fixture def s4_slit_gaps(): - return i03.s4_slit_gaps(connect_immediately=True, mock=True) + return i03.s4_slit_gaps.build(mock=True, connect_immediately=True) @pytest.fixture def synchrotron(): - synchrotron = i03.synchrotron(connect_immediately=True, mock=True) + synchrotron = i03.synchrotron.build(mock=True, connect_immediately=True) set_mock_value(synchrotron.synchrotron_mode, SynchrotronMode.USER) set_mock_value(synchrotron.top_up_start_countdown, 10) return synchrotron @@ -505,7 +505,7 @@ def oav(test_config_files): parameters = OAVConfigBeamCentre( test_config_files["zoom_params_file"], test_config_files["display_config"] ) - oav = i03.oav(connect_immediately=True, mock=True, params=parameters) + oav = i03.oav.build(mock=True, connect_immediately=True, params=parameters) zoom_levels_list = ["1.0x", "3.0x", "5.0x", "7.5x", "10.0x", "15.0x"] oav.zoom_controller._get_allowed_zoom_levels = AsyncMock( @@ -524,17 +524,17 @@ def oav(test_config_files): @pytest.fixture def flux(): - return i03.flux(connect_immediately=True, mock=True) + return i03.flux.build(connect_immediately=True, mock=True) @pytest.fixture def pin_tip(): - return i03.pin_tip_detection(connect_immediately=True, mock=True) + return i03.pin_tip_detection.build(connect_immediately=True, mock=True) @pytest.fixture def ophyd_pin_tip_detection(): - return i03.pin_tip_detection(connect_immediately=True, mock=True) + return i03.pin_tip_detection.build(connect_immediately=True, mock=True) @pytest.fixture() @@ -546,8 +546,8 @@ def transfocator(): @pytest.fixture -def robot(done_status): - robot = i03.robot(connect_immediately=True, mock=True) +def robot(): + robot = i03.robot.build(connect_immediately=True, mock=True) set_mock_value(robot.barcode, "BARCODE") @AsyncStatus.wrap @@ -576,7 +576,7 @@ def scintillator(aperture_scatterguard): @pytest.fixture def attenuator(): - attenuator = i03.attenuator(connect_immediately=True, mock=True) + attenuator = i03.attenuator.build(connect_immediately=True, mock=True) set_mock_value(attenuator.actual_transmission, 0.49118047952) @AsyncStatus.wrap @@ -597,7 +597,7 @@ def beamstop_phase1( "dodal.beamlines.i03.get_beamline_parameters", return_value=beamline_parameters, ): - beamstop = i03.beamstop(connect_immediately=True, mock=True) + beamstop = i03.beamstop.build(connect_immediately=True, mock=True) patch_all_motors(beamstop) set_mock_value(beamstop.x_mm.user_readback, 1.52) @@ -624,7 +624,7 @@ def xbpm_feedback( done_status, baton: Baton, # Ensure baton is cached with mock configuration ): - xbpm = i03.xbpm_feedback(connect_immediately=True, mock=True) + xbpm = i03.xbpm_feedback.build(connect_immediately=True, mock=True) xbpm.trigger = MagicMock(return_value=done_status) yield xbpm beamline_utils.clear_devices() @@ -641,14 +641,14 @@ def set_up_dcm(dcm: DCM, sim_run_engine: RunEngineSimulator): @pytest.fixture def dcm(sim_run_engine): - dcm = i03.dcm(connect_immediately=True, mock=True) + dcm = i03.dcm.build(connect_immediately=True, mock=True) set_up_dcm(dcm, sim_run_engine) yield dcm @pytest.fixture def vfm(): - vfm = i03.vfm(connect_immediately=True, mock=True) + vfm = i03.vfm.build(connect_immediately=True, mock=True) vfm.bragg_to_lat_lookup_table_path = ( "tests/test_data/test_beamline_vfm_lat_converter.txt" ) @@ -660,7 +660,7 @@ def vfm(): def lower_gonio( sim_run_engine: RunEngineSimulator, ): - lower_gonio = i03.lower_gonio(connect_immediately=True, mock=True) + lower_gonio = i03.lower_gonio.build(connect_immediately=True, mock=True) # Replace when https://github.com/bluesky/bluesky/issues/1906 is fixed def locate_gonio(_): @@ -675,7 +675,7 @@ def locate_gonio(_): @pytest.fixture def mirror_voltages(): - voltages = i03.mirror_voltages(connect_immediately=True, mock=True) + voltages = i03.mirror_voltages.build(connect_immediately=True, mock=True) voltages.voltage_lookup_table_path = "tests/test_data/test_mirror_focus.json" for vc in voltages.vertical_voltages.values(): vc.set = MagicMock(return_value=NullStatus()) @@ -686,33 +686,32 @@ def mirror_voltages(): @pytest.fixture -def undulator_dcm(sim_run_engine, undulator, dcm): - # This depends on the undulator and dcm as they must be connected as mocks first - undulator_dcm = i03.undulator_dcm( +def undulator_dcm(sim_run_engine): + undulator_dcm = i03.undulator_dcm.build( connect_immediately=True, mock=True, daq_configuration_path="tests/test_data/test_daq_configuration", ) set_up_dcm(undulator_dcm.dcm_ref(), sim_run_engine) yield undulator_dcm - beamline_utils.clear_devices() + # beamline_utils.clear_devices() @pytest.fixture def webcam() -> Generator[Webcam, Any, Any]: - webcam = i03.webcam(connect_immediately=True, mock=True) + webcam = i03.webcam.build(connect_immediately=True, mock=True) with patch.object(webcam, "_get_and_write_image"): yield webcam @pytest.fixture def thawer() -> Generator[Thawer, Any, Any]: - yield i03.thawer(connect_immediately=True, mock=True) + yield i03.thawer.build(connect_immediately=True, mock=True) @pytest.fixture def sample_shutter() -> Generator[ZebraShutter, Any, Any]: - yield i03.sample_shutter(connect_immediately=True, mock=True) + yield i03.sample_shutter.build(connect_immediately=True, mock=True) @pytest.fixture @@ -775,7 +774,7 @@ async def aperture_scatterguard(): ), ), ): - ap_sg = i03.aperture_scatterguard(connect_immediately=True, mock=True) + ap_sg = i03.aperture_scatterguard.build(connect_immediately=True, mock=True) with ( patch_all_motors(ap_sg), patch_motor(ap_sg.aperture.z, 2), @@ -867,7 +866,7 @@ def fake_create_rotation_devices( @pytest.fixture def zocalo(done_status): - zoc = i03.zocalo(connect_immediately=True, mock=True) + zoc = i03.zocalo.build(connect_immediately=True, mock=True) zoc.stage = MagicMock(return_value=done_status) zoc.unstage = MagicMock(return_value=done_status) return zoc @@ -907,7 +906,7 @@ async def create_mock_signals(devices_and_signals: dict[Device, dict[str, Any]]) await sig.connect(mock=True) setattr(device, name, sig) - panda = i03.panda(connect_immediately=True, mock=True) + panda = i03.panda.build(connect_immediately=True, mock=True) await set_mock_blocks( panda, { @@ -947,7 +946,7 @@ def mock_gridscan_kickoff_complete(gridscan: FastGridScanCommon): @pytest.fixture def panda_fast_grid_scan(): - scan = i03.panda_fast_grid_scan(connect_immediately=True, mock=True) + scan = i03.panda_fast_grid_scan.build(connect_immediately=True, mock=True) for signal in [scan.x_scan_valid, scan.y_scan_valid, scan.z_scan_valid]: set_mock_value(signal, 1) return scan @@ -976,20 +975,20 @@ async def hyperion_flyscan_xrc_composite( backlight=backlight, dcm=dcm, # We don't use the eiger fixture here because .unstage() is used in some tests - eiger=i03.eiger(connect_immediately=True, mock=True), + eiger=i03.eiger.build(mock=True), zebra_fast_grid_scan=fast_grid_scan, - flux=i03.flux(connect_immediately=True, mock=True), + flux=i03.flux.build(connect_immediately=True, mock=True), s4_slit_gaps=s4_slit_gaps, smargon=smargon, - undulator=i03.undulator(connect_immediately=True, mock=True), + undulator=i03.undulator.build(connect_immediately=True, mock=True), synchrotron=synchrotron, xbpm_feedback=xbpm_feedback, - zebra=i03.zebra(connect_immediately=True, mock=True), + zebra=i03.zebra.build(connect_immediately=True, mock=True), zocalo=zocalo, panda=panda, panda_fast_grid_scan=panda_fast_grid_scan, - robot=i03.robot(connect_immediately=True, mock=True), - sample_shutter=i03.sample_shutter(connect_immediately=True, mock=True), + robot=i03.robot.build(connect_immediately=True, mock=True), + sample_shutter=i03.sample_shutter.build(connect_immediately=True, mock=True), ) fake_composite.eiger.stage = MagicMock(return_value=done_status) diff --git a/tests/system_tests/conftest.py b/tests/system_tests/conftest.py index 9bd5b6f75f..c52568687c 100644 --- a/tests/system_tests/conftest.py +++ b/tests/system_tests/conftest.py @@ -167,7 +167,7 @@ def oav_for_system_test(test_config_files, next_oav_system_test_image): parameters = OAVConfigBeamCentre( test_config_files["zoom_params_file"], test_config_files["display_config"] ) - oav = i03.oav(connect_immediately=True, mock=True, params=parameters) + oav = i03.oav.build(connect_immediately=True, mock=True, params=parameters) set_mock_value(oav.cam.array_size_x, 1024) set_mock_value(oav.cam.array_size_y, 768) diff --git a/tests/system_tests/hyperion/external_interaction/conftest.py b/tests/system_tests/hyperion/external_interaction/conftest.py index 3775b605f9..8b0257cb07 100644 --- a/tests/system_tests/hyperion/external_interaction/conftest.py +++ b/tests/system_tests/hyperion/external_interaction/conftest.py @@ -216,7 +216,7 @@ async def zocalo_for_fake_zocalo(zocalo_env) -> ZocaloResults: @pytest.fixture def zocalo_for_system_test() -> Generator[ZocaloResults, None, None]: - zocalo = i03.zocalo(connect_immediately=True, mock=True) + zocalo = i03.zocalo.build(connect_immediately=True, mock=True) zocalo.timeout_s = 10 old_zocalo_trigger = zocalo.trigger zocalo.my_zocalo_result = deepcopy(TEST_RESULT_MEDIUM) diff --git a/tests/unit_tests/common/experiment_plans/inner_plans/test_read_hardware.py b/tests/unit_tests/common/experiment_plans/inner_plans/test_read_hardware.py index 3a26f815bf..633cd51800 100644 --- a/tests/unit_tests/common/experiment_plans/inner_plans/test_read_hardware.py +++ b/tests/unit_tests/common/experiment_plans/inner_plans/test_read_hardware.py @@ -72,13 +72,13 @@ async def fake_composite( aperture_scatterguard=aperture_scatterguard, attenuator=attenuator, dcm=dcm, - flux=i03.flux(connect_immediately=True, mock=True), - s4_slit_gaps=i03.s4_slit_gaps(connect_immediately=True, mock=True), - undulator=i03.undulator(connect_immediately=True, mock=True), + flux=i03.flux.build(connect_immediately=True, mock=True), + s4_slit_gaps=i03.s4_slit_gaps.build(connect_immediately=True, mock=True), + undulator=i03.undulator.build(connect_immediately=True, mock=True), synchrotron=synchrotron, robot=robot, smargon=smargon, - eiger=eiger(connect_immediately=True, mock=True), + eiger=eiger.build(mock=True), ) return fake_composite diff --git a/tests/unit_tests/common/experiment_plans/test_common_flyscan_xray_centre_plan.py b/tests/unit_tests/common/experiment_plans/test_common_flyscan_xray_centre_plan.py index 91f18e5b1c..f265039c2f 100644 --- a/tests/unit_tests/common/experiment_plans/test_common_flyscan_xray_centre_plan.py +++ b/tests/unit_tests/common/experiment_plans/test_common_flyscan_xray_centre_plan.py @@ -217,7 +217,7 @@ def test_waits_for_motion_program( done_status: Status, ): fake_fgs_composite.eiger.unstage = MagicMock(return_value=done_status) - fgs = i03.zebra_fast_grid_scan(connect_immediately=True, mock=True) + fgs = i03.zebra_fast_grid_scan.build(connect_immediately=True, mock=True) fgs.KICKOFF_TIMEOUT = 0.1 fgs.complete = MagicMock(return_value=done_status) set_mock_value(fgs.motion_program.running, 1) diff --git a/tests/unit_tests/common/experiment_plans/test_grid_detection_plan.py b/tests/unit_tests/common/experiment_plans/test_grid_detection_plan.py index d7fe0baa07..2006f7ab03 100644 --- a/tests/unit_tests/common/experiment_plans/test_grid_detection_plan.py +++ b/tests/unit_tests/common/experiment_plans/test_grid_detection_plan.py @@ -59,7 +59,7 @@ def fake_devices( params = OAVConfigBeamCentre( test_config_files["zoom_params_file"], test_config_files["display_config"] ) - oav = i03.oav(connect_immediately=True, mock=True, params=params) + oav = i03.oav.build(connect_immediately=True, mock=True, params=params) zoom_levels_list = ["1.0x", "3.0x", "5.0x", "7.5x", "10.0x", "15.0x"] oav.zoom_controller._get_allowed_zoom_levels = AsyncMock( return_value=zoom_levels_list @@ -68,7 +68,7 @@ def fake_devices( set_mock_value(oav.grid_snapshot.x_size, 1024) set_mock_value(oav.grid_snapshot.y_size, 768) - pin_tip_detection = i03.pin_tip_detection(connect_immediately=True, mock=True) + pin_tip_detection = i03.pin_tip_detection.build(connect_immediately=True, mock=True) pin_tip_detection._get_tip_and_edge_data = AsyncMock( side_effect=[X_Y_EDGE_DATA, X_Z_EDGE_DATA] ) diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index c3e096ecce..3fd45d0ce8 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -306,7 +306,9 @@ def run_generic_ispyb_handler_setup( @pytest.fixture async def zebra_fast_grid_scan(): - zebra_fast_grid_scan = i03.zebra_fast_grid_scan(connect_immediately=True, mock=True) + zebra_fast_grid_scan = i03.zebra_fast_grid_scan.build( + connect_immediately=True, mock=True + ) set_mock_value(zebra_fast_grid_scan.device_scan_invalid, 0.0) set_mock_value(zebra_fast_grid_scan.x_scan_valid, 1.0) set_mock_value(zebra_fast_grid_scan.y_scan_valid, 1.0) @@ -330,7 +332,7 @@ async def fake_fgs_composite( ): fake_composite = FlyScanEssentialDevices( # We don't use the eiger fixture here because .unstage() is used in some tests - eiger=i03.eiger(connect_immediately=True, mock=True), + eiger=i03.eiger.build(mock=True), smargon=smargon, synchrotron=synchrotron, zocalo=zocalo, diff --git a/tests/unit_tests/hyperion/device_setup_plans/test_utils.py b/tests/unit_tests/hyperion/device_setup_plans/test_utils.py index cabf0b9c12..7d55701457 100644 --- a/tests/unit_tests/hyperion/device_setup_plans/test_utils.py +++ b/tests/unit_tests/hyperion/device_setup_plans/test_utils.py @@ -14,7 +14,7 @@ @pytest.fixture() def mock_eiger(): - eiger = i03.eiger(mock=True) + eiger = i03.eiger.build(mock=True) eiger.detector_params = MagicMock() eiger.async_stage = MagicMock() eiger.disarm_detector = MagicMock() diff --git a/tests/unit_tests/hyperion/experiment_plans/test_optimise_attenuation_plan.py b/tests/unit_tests/hyperion/experiment_plans/test_optimise_attenuation_plan.py index 12b60b59ad..8503ae5768 100644 --- a/tests/unit_tests/hyperion/experiment_plans/test_optimise_attenuation_plan.py +++ b/tests/unit_tests/hyperion/experiment_plans/test_optimise_attenuation_plan.py @@ -41,8 +41,8 @@ def mock_emit(): @pytest.fixture async def fake_composite(attenuator) -> OptimizeAttenuationComposite: - sample_shutter = i03.sample_shutter(connect_immediately=True, mock=True) - xspress3mini = i03.xspress3mini(connect_immediately=True, mock=True) + sample_shutter = i03.sample_shutter.build(connect_immediately=True, mock=True) + xspress3mini = i03.xspress3mini.build(connect_immediately=True, mock=True) return OptimizeAttenuationComposite( sample_shutter=sample_shutter, xspress3mini=xspress3mini, attenuator=attenuator @@ -272,12 +272,12 @@ def test_deadtime_calc_new_transmission_gets_correct_value( ) -def test_deadtime_calc_new_transmission_raises_error_on_low_ransmission(): +def test_deadtime_calc_new_transmission_raises_error_on_low_transmission(): with pytest.raises(AttenuationOptimisationFailedError): deadtime_calc_new_transmission(Direction.NEGATIVE, 1e-6, 2, 1, 1e-6) -def test_total_count_calc_new_transmission_raises_error_on_low_ransmission( +def test_total_count_calc_new_transmission_raises_error_on_low_transmission( run_engine: RunEngine, fake_composite_mocked_sets: OptimizeAttenuationComposite ): set_mock_value( diff --git a/tests/unit_tests/hyperion/experiment_plans/test_robot_load_then_centre.py b/tests/unit_tests/hyperion/experiment_plans/test_robot_load_then_centre.py index dd4dd22a83..8d7c0b1b1c 100644 --- a/tests/unit_tests/hyperion/experiment_plans/test_robot_load_then_centre.py +++ b/tests/unit_tests/hyperion/experiment_plans/test_robot_load_then_centre.py @@ -487,3 +487,8 @@ def test_box_size_passed_through_to_gridscan( ) detect_grid_call = mock_detect_grid.mock_calls[0] assert detect_grid_call.args[1].box_size_um == 25 + + +async def test_multiple_devices(dcm, undulator, undulator_dcm): + await undulator.current_gap.get_value() + await undulator_dcm.set(100) From c62e9fb07ab1bc2fb2652c8fac15d5bb1aa95b99 Mon Sep 17 00:00:00 2001 From: Dominic Oram Date: Tue, 18 Nov 2025 13:59:31 +0000 Subject: [PATCH 2/4] Pin dodal --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9cdc036bd0..f7d840a0f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ dependencies = [ "ophyd >= 1.10.5", "ophyd-async >= 0.13.7", "bluesky >= 1.14.6", - "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@main", + "dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@i03-device-manager", ] From c2a751513e413bc45c5fc14935cd486fdb5a44f5 Mon Sep 17 00:00:00 2001 From: Robert Tuck Date: Tue, 18 Nov 2025 16:56:44 +0000 Subject: [PATCH 3/4] Fix unit tests --- src/mx_bluesky/hyperion/utils/context.py | 4 ++-- tests/unit_tests/hyperion/test_baton_handler.py | 12 +++++------- tests/unit_tests/hyperion/test_main_system.py | 6 +++--- tests/unit_tests/hyperion/utils/test_context.py | 8 ++++---- tests/unit_tests/t01.py | 8 +++++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/mx_bluesky/hyperion/utils/context.py b/src/mx_bluesky/hyperion/utils/context.py index 84f9b43993..4010156aa2 100644 --- a/src/mx_bluesky/hyperion/utils/context.py +++ b/src/mx_bluesky/hyperion/utils/context.py @@ -27,8 +27,8 @@ def clear_all_device_caches(context: BlueskyContext): def setup_devices(context: BlueskyContext, dev_mode: bool): - _, exceptions = context.with_dodal_module( - get_beamline_based_on_environment_variable(), + _, exceptions = context.with_device_manager( + get_beamline_based_on_environment_variable().devices, mock=dev_mode, ) if exceptions: diff --git a/tests/unit_tests/hyperion/test_baton_handler.py b/tests/unit_tests/hyperion/test_baton_handler.py index 97fbff5afb..12b10ef7f9 100644 --- a/tests/unit_tests/hyperion/test_baton_handler.py +++ b/tests/unit_tests/hyperion/test_baton_handler.py @@ -125,14 +125,13 @@ def mock_load_module(module, **kwargs): context.register_device(device) return {d.name: d for d in devices}, {} - context.with_dodal_module( - get_beamline_based_on_environment_variable(), + context.with_device_manager( + get_beamline_based_on_environment_variable().devices, mock=True, - fake_with_ophyd_sim=True, ) baton_with_requested_user(context, HYPERION_USER) - with patch.object(context, "with_dodal_module", mock_load_module): + with patch.object(context, "with_device_manager", mock_load_module): yield context @@ -174,10 +173,9 @@ def dont_connect(*args, **kwargs): patch.dict(os.environ, {"BEAMLINE": "i03"}), ): context = BlueskyContext(run_engine=faked_run_engine) - context.with_dodal_module( - get_beamline_based_on_environment_variable(), + context.with_device_manager( + get_beamline_based_on_environment_variable().devices, mock=True, - fake_with_ophyd_sim=True, ) yield msgs, context diff --git a/tests/unit_tests/hyperion/test_main_system.py b/tests/unit_tests/hyperion/test_main_system.py index 15a3dd4093..e9e11bd195 100644 --- a/tests/unit_tests/hyperion/test_main_system.py +++ b/tests/unit_tests/hyperion/test_main_system.py @@ -540,11 +540,11 @@ def test_when_context_created_then_contains_expected_number_of_plans( {"BEAMLINE": "i03"}, ): with patch( - "mx_bluesky.hyperion.utils.context.BlueskyContext.with_dodal_module", + "mx_bluesky.hyperion.utils.context.BlueskyContext.with_device_manager", return_value=({}, {}), - ) as mock_with_dodal_module: + ) as mock_with_device_manager: context = setup_context(dev_mode=dev_mode) - mock_with_dodal_module.assert_called_once_with(ANY, mock=dev_mode) + mock_with_device_manager.assert_called_once_with(ANY, mock=dev_mode) plan_names = context.plans.keys() # assert "rotation_scan" in plan_names diff --git a/tests/unit_tests/hyperion/utils/test_context.py b/tests/unit_tests/hyperion/utils/test_context.py index a85f2e54f5..b4d92459f6 100644 --- a/tests/unit_tests/hyperion/utils/test_context.py +++ b/tests/unit_tests/hyperion/utils/test_context.py @@ -1,4 +1,4 @@ -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import MagicMock, patch import pydantic import pytest @@ -81,9 +81,9 @@ def test_setup_devices_raises_on_exception(use_beamline_t01, run_engine: RunEngi context = BlueskyContext(run_engine=run_engine) with patch.object( - use_beamline_t01.baton(), - "connect", - AsyncMock(side_effect=RuntimeError("Simulated exception")), + context, + "with_device_manager", + return_value=({}, {"baton": RuntimeError("Simulated exception")}), ): with pytest.raises(ExceptionGroup): setup_devices(context, True) diff --git a/tests/unit_tests/t01.py b/tests/unit_tests/t01.py index 8a6b407620..875dae44f6 100644 --- a/tests/unit_tests/t01.py +++ b/tests/unit_tests/t01.py @@ -6,7 +6,7 @@ use_beamline_t01() """ -from dodal.common.beamlines.beamline_utils import device_factory +from dodal.device_manager import DeviceManager from dodal.devices.baton import Baton from dodal.devices.xbpm_feedback import XBPMFeedback from dodal.utils import BeamlinePrefix, get_beamline_name @@ -14,8 +14,10 @@ BL = get_beamline_name("t01") PREFIX = BeamlinePrefix(BL) +devices = DeviceManager() -@device_factory() + +@devices.factory() def baton() -> Baton: """Get the i03 baton device, instantiate it if it hasn't already been. If this is called when already instantiated in i03, it will return the existing object. @@ -23,7 +25,7 @@ def baton() -> Baton: return Baton(f"{PREFIX.beamline_prefix}-CS-BATON-01:") -@device_factory() +@devices.factory() def xbpm_feedback() -> XBPMFeedback: """Get the i03 XBPM feeback device, instantiate it if it hasn't already been. If this is called when already instantiated in i03, it will return the existing object. From 7a19875324a7b25ed662734d0d62e12cfdde6d67 Mon Sep 17 00:00:00 2001 From: Dominic Oram Date: Thu, 27 Nov 2025 13:39:15 +0000 Subject: [PATCH 4/4] Pin blueapi --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d185103729..f62b729a85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ dependencies = [ # as by default pip-install will not upgrade to a pre-release. # "daq-config-server>=v1.0.0-rc.2", - "blueapi >= 1.6.3", + "blueapi >= 1.8.0", "ophyd >= 1.10.5", "ophyd-async >= 0.14.0", "bluesky >= 1.14.6",