Skip to content

Commit 7408625

Browse files
authored
Merge pull request #23 from DiamondLightSource/testRigFixes
Tidy up util module(s) and tests
2 parents e9fba75 + bff1d6f commit 7408625

File tree

9 files changed

+157
-94
lines changed

9 files changed

+157
-94
lines changed

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ dependencies = [
2020
"dataclasses-json",
2121
"pillow",
2222
"requests",
23-
"pydantic",
2423
]
2524
dynamic = ["version"]
2625
license.file = "LICENSE"

src/dodal/adsim.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
1-
import socket
2-
3-
from pydantic import BaseSettings
1+
import os
42

53
from dodal.devices.adsim import SimStage
64
from dodal.devices.areadetector import AdSimDetector
75

6+
from .utils import get_hostname
87

9-
# Settings can be customized via environment variables
10-
class Settings(BaseSettings):
11-
pv_prefix: str = socket.gethostname().split(".")[0]
12-
13-
14-
_settings = Settings()
8+
# Default prefix to hostname unless overriden with export PREFIX=<prefix>
9+
PREFIX: str = os.environ.get("PREFIX", get_hostname())
1510

1611

1712
def stage(name: str = "sim_motors") -> SimStage:
18-
return SimStage(name=name, prefix=f"{_settings.pv_prefix}-MO-SIM-01:")
13+
return SimStage(name=name, prefix=f"{PREFIX}-MO-SIM-01:")
1914

2015

2116
def det(name: str = "adsim") -> AdSimDetector:
22-
return AdSimDetector(name=name, prefix=f"{_settings.pv_prefix}-AD-SIM-01:")
17+
return AdSimDetector(name=name, prefix=f"{PREFIX}-AD-SIM-01:")

src/dodal/p45.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,22 @@
1-
from pydantic import BaseSettings
2-
31
from dodal.devices.areadetector import AdAravisDetector
42
from dodal.devices.p45 import Choppers, TomoStageWithStretchAndSkew
53

4+
from .utils import BeamlinePrefix
65

7-
# Settings can be customized via environment variables
8-
class Settings(BaseSettings):
9-
pv_prefix: str = "BL45P"
10-
11-
12-
_settings = Settings()
6+
PREFIX: str = BeamlinePrefix("p45").beamline_prefix
137

148

15-
def sample_sample(name: str = "sample_stage") -> TomoStageWithStretchAndSkew:
16-
return TomoStageWithStretchAndSkew(
17-
name=name, prefix=f"{_settings.pv_prefix}-MO-STAGE-01:"
18-
)
9+
def sample(name: str = "sample_stage") -> TomoStageWithStretchAndSkew:
10+
return TomoStageWithStretchAndSkew(name=name, prefix=f"{PREFIX}-MO-STAGE-01:")
1911

2012

2113
def choppers(name: str = "chopper") -> Choppers:
22-
return Choppers(name=name, prefix=f"{_settings.pv_prefix}-MO-CHOP-01:")
14+
return Choppers(name=name, prefix=f"{PREFIX}-MO-CHOP-01:")
2315

2416

2517
def det(name: str = "det") -> AdAravisDetector:
26-
return AdAravisDetector(name=name, prefix=f"{_settings.pv_prefix}-EA-MAP-01:")
18+
return AdAravisDetector(name=name, prefix=f"{PREFIX}-EA-MAP-01:")
2719

2820

2921
def diff(name: str = "diff") -> AdAravisDetector:
30-
return AdAravisDetector(name=name, prefix=f"{_settings.pv_prefix}-EA-DIFF-01:")
22+
return AdAravisDetector(name=name, prefix=f"{PREFIX}-EA-DIFF-01:")

src/dodal/util.py

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/dodal/utils.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,48 @@
1+
import inspect
2+
import socket
13
from collections import namedtuple
24
from dataclasses import dataclass
5+
from importlib import import_module
6+
from inspect import signature
37
from os import environ
4-
from typing import Optional
8+
from types import ModuleType
9+
from typing import Any, Callable, Dict, Iterable, Optional, Type, Union
10+
11+
from bluesky.protocols import (
12+
Checkable,
13+
Configurable,
14+
Flyable,
15+
HasHints,
16+
HasName,
17+
HasParent,
18+
Movable,
19+
Pausable,
20+
Readable,
21+
Stageable,
22+
Stoppable,
23+
Subscribable,
24+
Triggerable,
25+
WritesExternalAssets,
26+
)
27+
28+
#: Protocols defining interface to hardware
29+
BLUESKY_PROTOCOLS = [
30+
Checkable,
31+
Flyable,
32+
HasHints,
33+
HasName,
34+
HasParent,
35+
Movable,
36+
Pausable,
37+
Readable,
38+
Stageable,
39+
Stoppable,
40+
Subscribable,
41+
WritesExternalAssets,
42+
Configurable,
43+
Triggerable,
44+
]
45+
546

647
Point2D = namedtuple("Point2D", ["x", "y"])
748
Point3D = namedtuple("Point3D", ["x", "y", "z"])
@@ -15,6 +56,10 @@ def get_beamline_name(ixx: str) -> str:
1556
return bl
1657

1758

59+
def get_hostname() -> str:
60+
return socket.gethostname().split(".")[0]
61+
62+
1863
@dataclass
1964
class BeamlinePrefix:
2065
ixx: str
@@ -24,3 +69,29 @@ def __post_init__(self):
2469
self.suffix = self.ixx[0].upper() if not self.suffix else self.suffix
2570
self.beamline_prefix = f"BL{self.ixx[1:3]}{self.suffix}"
2671
self.insertion_prefix = f"SR{self.ixx[1:3]}{self.suffix}"
72+
73+
74+
def make_all_devices(module: Union[str, ModuleType, None] = None) -> Dict[str, Any]:
75+
if isinstance(module, str) or module is None:
76+
module = import_module(module or __name__)
77+
factories = collect_factories(module)
78+
return {device.name: device for device in map(lambda factory: factory(), factories)}
79+
80+
81+
def collect_factories(module: ModuleType) -> Iterable[Callable[..., Any]]:
82+
for var in module.__dict__.values():
83+
if callable(var) and _is_device_factory(var):
84+
yield var
85+
86+
87+
def _is_device_factory(func: Callable[..., Any]) -> bool:
88+
return_type = signature(func).return_annotation
89+
return _is_device_type(return_type)
90+
91+
92+
def _is_device_type(obj: Type[Any]) -> bool:
93+
is_class = inspect.isclass(obj)
94+
follows_protocols = any(
95+
map(lambda protocol: isinstance(obj, protocol), BLUESKY_PROTOCOLS)
96+
)
97+
return is_class and follows_protocols

tests/__init__.py

Whitespace-only changes.

tests/devices/system_tests/test_aperturescatterguard_system.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from typing import Any, Dict, List, Tuple, cast
24

35
import bluesky.plan_stubs as bps
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from unittest.mock import MagicMock, patch
2+
3+
from bluesky.protocols import Readable
4+
from ophyd import EpicsMotor
5+
6+
from dodal.utils import collect_factories, get_hostname, make_all_devices
7+
8+
9+
def test_finds_device_factories() -> None:
10+
import tests.fake_beamline as fake_beamline
11+
12+
factories = set(collect_factories(fake_beamline))
13+
14+
from tests.fake_beamline import device_a, device_b, device_c
15+
16+
assert {device_a, device_b, device_c} == factories
17+
18+
19+
def test_makes_devices() -> None:
20+
import tests.fake_beamline as fake_beamline
21+
22+
devices = make_all_devices(fake_beamline)
23+
assert {"readable", "motor", "cryo"} == devices.keys()
24+
25+
26+
def test_makes_devices_with_module_name() -> None:
27+
devices = make_all_devices("tests.fake_beamline")
28+
assert {"readable", "motor", "cryo"} == devices.keys()
29+
30+
31+
def test_get_hostname() -> None:
32+
with patch("dodal.utils.socket.gethostname") as mock:
33+
mock.return_value = "a.b.c"
34+
assert get_hostname() == "a"
35+
36+
37+
def device_a() -> Readable:
38+
return MagicMock()
39+
40+
41+
def device_b() -> EpicsMotor:
42+
return MagicMock()

tests/fake_beamline.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from unittest.mock import MagicMock
2+
3+
from bluesky.protocols import Readable
4+
from ophyd import EpicsMotor
5+
6+
from dodal.devices.cryostream import Cryo
7+
8+
9+
def device_a() -> Readable:
10+
return _mock_with_name("readable")
11+
12+
13+
def device_b() -> EpicsMotor:
14+
return _mock_with_name("motor")
15+
16+
17+
def device_c() -> Cryo:
18+
return _mock_with_name("cryo")
19+
20+
21+
def not_device() -> int:
22+
return 5
23+
24+
25+
def _mock_with_name(name: str) -> MagicMock:
26+
mock = MagicMock()
27+
mock.name = name
28+
return mock

0 commit comments

Comments
 (0)