diff --git a/src/blueapi/core/context.py b/src/blueapi/core/context.py index 9bf29aec3..fed2ebc94 100644 --- a/src/blueapi/core/context.py +++ b/src/blueapi/core/context.py @@ -432,12 +432,13 @@ def __get_pydantic_core_schema__( ) -> CoreSchema: def valid(value): val = self.find_device(value) - if not val or not is_compatible( - val, cls.origin or target, cls.args - ): + if not val: + raise ValueError(f"Device {value} cannot be found") + elif not is_compatible(val, cls.origin or target, cls.args): + actual = qualified_name(type(val)) required = qualified_generic_name(target) raise ValueError( - f"Device {value} is not of type {required}" + f"Device {value} ({actual}) is not of type {required}" ) return val diff --git a/tests/unit_tests/worker/test_task_worker.py b/tests/unit_tests/worker/test_task_worker.py index 2793e8807..71040e420 100644 --- a/tests/unit_tests/worker/test_task_worker.py +++ b/tests/unit_tests/worker/test_task_worker.py @@ -10,7 +10,7 @@ import pydantic import pytest -from bluesky.protocols import Movable, Status +from bluesky.protocols import Movable, Readable, Status from bluesky.utils import MsgGenerator from dodal.common import inject from dodal.common.types import UpdatingPathProvider @@ -681,10 +681,27 @@ def missing_injection(dev: FakeDevice = inject("does_not_exist")) -> MsgGenerato yield from () context.register_plan(missing_injection) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Device does_not_exist cannot be found"): Task(name="missing_injection").prepare_params(context) +def test_invalid_injected_devices_fail_early( + context: BlueskyContext, +): + def invalid_injection(dev: Readable = inject("fake_device")) -> MsgGenerator: + yield from () + + context.register_plan(invalid_injection) + with pytest.raises( + ValueError, + match=( + r"Device fake_device \(test_task_worker.FakeDevice\) " + "is not of type bluesky.protocols.Readable" + ), + ): + Task(name="invalid_injection").prepare_params(context) + + @patch("blueapi.worker.task_worker.plan_tag_filter_context") def test_worker_uses_plan_tag_filter_context( mock_context: Mock, inert_worker: TaskWorker