diff --git a/docs/how-to/include-devices-in-plans.md b/docs/how-to/include-devices-in-plans.md new file mode 100644 index 0000000000..644265d06d --- /dev/null +++ b/docs/how-to/include-devices-in-plans.md @@ -0,0 +1,37 @@ +# Include Devices in Plans + +There are two main ways to include dodal devices in plans + +## 1. Pass as Argument + +```python +import bluesky.plans as bp + +from bluesky.protocols import Readable +from bluesky.utils import MsgGenerator +from dodal.beamlines import i22 + +def my_plan(detector: Readable) -> MsgGenerator: + yield from bp.count([detector]) + +RE(my_plan(i22.saxs())) +``` + +This is useful for generic plans that can run on a variety of devices and are not designed with any specific device in mind. + +## 2. Pass as Default Argument + +```python +import bluesky.plans as bp + +from bluesky.protocols import Readable +from bluesky.utils import MsgGenerator +from dodal.beamlines import i22 + +def my_plan(detector: Readable = i22.saxs(connect_immediately=False)) -> MsgGenerator: + yield from bp.count([detector]) + +RE(my_plan())) +``` + +This is useful for plans that will usually, but not exclusively, use the same device or that are designed to only ever work with a specific device. diff --git a/src/dodal/common/coordination.py b/src/dodal/common/coordination.py index e04edefc21..9aa8242849 100644 --- a/src/dodal/common/coordination.py +++ b/src/dodal/common/coordination.py @@ -1,4 +1,6 @@ import uuid +import warnings +from textwrap import dedent from typing import Any from dodal.common.types import Group @@ -37,4 +39,22 @@ def scan(x: Movable = inject("stage_x"), start: float = 0.0 ...) """ + warnings.warn( + dedent(""" + Inject is deprecated, users are now expected to call the device factory + functions in dodal directly, these will cache devices as singletons after + they have been called once. For example: + + from bluesky.protocols import Readable + from bluesky.utils import MsgGenerator + from dodal.beamlines import i22 + + def my_plan(detector: Readable = i22.saxs(connect_immediately=False)) -> MsgGenerator: + ... + + Where previously the default would have been inject("saxs") + """), + DeprecationWarning, + stacklevel=2, + ) return name diff --git a/tests/common/test_coordination.py b/tests/common/test_coordination.py index 8c0ec6adad..d206d89c80 100644 --- a/tests/common/test_coordination.py +++ b/tests/common/test_coordination.py @@ -16,6 +16,12 @@ def test_group_uid(group: str): assert not gid.endswith(f"{group}-") +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_inject_returns_value(): + assert inject("foo") == "foo" + + +@pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_type_checking_ignores_inject(): def example_function(x: Movable = inject("foo")) -> MsgGenerator: # noqa: B008 yield from {} @@ -25,3 +31,13 @@ def example_function(x: Movable = inject("foo")) -> MsgGenerator: # noqa: B008 x: Parameter = signature(example_function).parameters["x"] assert x.annotation == Movable assert x.default == "foo" + + +def test_inject_is_deprecated(): + with pytest.raises( + DeprecationWarning, + match="Inject is deprecated, users are now expected to call the device factory", + ): + + def example_function(x: Movable = inject("foo")) -> MsgGenerator: # noqa: B008 + yield from {}