ophyd-async 0.14.1__py3-none-any.whl → 0.14.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ophyd_async/_version.py +2 -2
- ophyd_async/core/_derived_signal.py +56 -23
- ophyd_async/core/_device_filler.py +28 -3
- {ophyd_async-0.14.1.dist-info → ophyd_async-0.14.2.dist-info}/METADATA +1 -1
- {ophyd_async-0.14.1.dist-info → ophyd_async-0.14.2.dist-info}/RECORD +8 -8
- {ophyd_async-0.14.1.dist-info → ophyd_async-0.14.2.dist-info}/WHEEL +0 -0
- {ophyd_async-0.14.1.dist-info → ophyd_async-0.14.2.dist-info}/licenses/LICENSE +0 -0
- {ophyd_async-0.14.1.dist-info → ophyd_async-0.14.2.dist-info}/top_level.txt +0 -0
ophyd_async/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.14.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 14,
|
|
31
|
+
__version__ = version = '0.14.2'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 14, 2)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
1
|
+
import functools
|
|
2
|
+
from collections.abc import Awaitable, Callable, Mapping
|
|
3
|
+
from inspect import Parameter, signature
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Generic,
|
|
7
|
+
TypeVar,
|
|
8
|
+
get_args,
|
|
9
|
+
get_origin,
|
|
10
|
+
get_type_hints,
|
|
11
|
+
is_typeddict,
|
|
12
|
+
)
|
|
3
13
|
|
|
4
14
|
from bluesky.protocols import Locatable
|
|
5
15
|
|
|
@@ -53,12 +63,13 @@ class DerivedSignalFactory(Generic[TransformT]):
|
|
|
53
63
|
# Populate expected parameters and types
|
|
54
64
|
expected = {
|
|
55
65
|
**{k: f.annotation for k, f in transform_cls.model_fields.items()},
|
|
56
|
-
**
|
|
57
|
-
k: v
|
|
58
|
-
for k, v in get_type_hints(transform_cls.raw_to_derived).items()
|
|
59
|
-
if k not in {"self", "return"}
|
|
60
|
-
},
|
|
66
|
+
**_get_params_types_dict(transform_cls.raw_to_derived),
|
|
61
67
|
}
|
|
68
|
+
if empty_keys := [k for k, v in expected.items() if v == Parameter.empty]:
|
|
69
|
+
raise TypeError(
|
|
70
|
+
f"{transform_cls.raw_to_derived} is missing a type "
|
|
71
|
+
f"hint for arguments: {empty_keys}"
|
|
72
|
+
)
|
|
62
73
|
|
|
63
74
|
# Populate received parameters and types
|
|
64
75
|
# Use Primitive's type, Signal's datatype,
|
|
@@ -76,7 +87,19 @@ class DerivedSignalFactory(Generic[TransformT]):
|
|
|
76
87
|
f"Expected the following to be passed as keyword arguments "
|
|
77
88
|
f"{expected}, got {received}"
|
|
78
89
|
)
|
|
79
|
-
|
|
90
|
+
if set(expected.keys()) - set(received.keys()):
|
|
91
|
+
raise TypeError(msg)
|
|
92
|
+
|
|
93
|
+
for k in set(expected.keys()):
|
|
94
|
+
if isinstance(expected[k], type):
|
|
95
|
+
if not issubclass(received[k], expected[k]):
|
|
96
|
+
raise TypeError(msg)
|
|
97
|
+
elif isinstance(expected[k], TypeVar):
|
|
98
|
+
bound = expected[k].__bound__
|
|
99
|
+
if isinstance(bound, type) and not issubclass(
|
|
100
|
+
received[k], bound
|
|
101
|
+
):
|
|
102
|
+
raise TypeError(msg)
|
|
80
103
|
self._set_derived_takes_dict = (
|
|
81
104
|
is_typeddict(_get_first_arg_datatype(set_derived)) if set_derived else False
|
|
82
105
|
)
|
|
@@ -195,28 +218,28 @@ def _get_return_datatype(func: Callable[..., SignalDatatypeT]) -> type[SignalDat
|
|
|
195
218
|
def _get_first_arg_datatype(
|
|
196
219
|
func: Callable[[SignalDatatypeT], Any],
|
|
197
220
|
) -> type[SignalDatatypeT]:
|
|
198
|
-
args =
|
|
199
|
-
args.pop("return", None)
|
|
221
|
+
args = _get_params_types_dict(func)
|
|
200
222
|
if not args:
|
|
201
223
|
msg = f"{func} does not have a type hinted argument"
|
|
202
224
|
raise TypeError(msg)
|
|
203
225
|
return list(args.values())[0]
|
|
204
226
|
|
|
205
227
|
|
|
228
|
+
def _get_params_types_dict(inspected_function: Callable) -> Mapping[str, Any]:
|
|
229
|
+
sig = signature(inspected_function, eval_str=True)
|
|
230
|
+
exclude_keys = {"self", "args", "kwargs", "cls"}
|
|
231
|
+
return {k: v.annotation for k, v in sig.parameters.items() if k not in exclude_keys}
|
|
232
|
+
|
|
233
|
+
|
|
206
234
|
def _make_factory(
|
|
207
|
-
|
|
235
|
+
raw_to_derived_func: Callable[..., SignalDatatypeT] | None = None,
|
|
208
236
|
set_derived: Callable[[SignalDatatypeT], Awaitable[None]] | None = None,
|
|
209
237
|
raw_devices_and_constants: dict[str, Device | Primitive] | None = None,
|
|
210
238
|
) -> DerivedSignalFactory:
|
|
211
|
-
if
|
|
239
|
+
if raw_to_derived_func:
|
|
212
240
|
|
|
213
241
|
class DerivedTransform(Transform):
|
|
214
|
-
|
|
215
|
-
return {"value": raw_to_derived(**kwargs)}
|
|
216
|
-
|
|
217
|
-
# Update the signature for raw_to_derived to match what we are passed as this
|
|
218
|
-
# will be checked in DerivedSignalFactory
|
|
219
|
-
DerivedTransform.raw_to_derived.__annotations__ = get_type_hints(raw_to_derived)
|
|
242
|
+
raw_to_derived = _dict_wrapper(raw_to_derived_func)
|
|
220
243
|
|
|
221
244
|
return DerivedSignalFactory(
|
|
222
245
|
DerivedTransform,
|
|
@@ -245,7 +268,7 @@ def derived_signal_r(
|
|
|
245
268
|
The names of these arguments must match the arguments of raw_to_derived.
|
|
246
269
|
"""
|
|
247
270
|
factory = _make_factory(
|
|
248
|
-
|
|
271
|
+
raw_to_derived_func=raw_to_derived,
|
|
249
272
|
raw_devices_and_constants=raw_devices_and_constants,
|
|
250
273
|
)
|
|
251
274
|
return factory.derived_signal_r(
|
|
@@ -278,16 +301,16 @@ def derived_signal_rw(
|
|
|
278
301
|
The names of these arguments must match the arguments of raw_to_derived.
|
|
279
302
|
"""
|
|
280
303
|
raw_to_derived_datatype = _get_return_datatype(raw_to_derived)
|
|
281
|
-
|
|
282
|
-
if raw_to_derived_datatype !=
|
|
304
|
+
set_derived_arg_datatype = _get_first_arg_datatype(set_derived)
|
|
305
|
+
if raw_to_derived_datatype != set_derived_arg_datatype:
|
|
283
306
|
msg = (
|
|
284
307
|
f"{raw_to_derived} has datatype {raw_to_derived_datatype} "
|
|
285
|
-
f"!= {
|
|
308
|
+
f"!= {set_derived_arg_datatype} datatype {set_derived_arg_datatype}"
|
|
286
309
|
)
|
|
287
310
|
raise TypeError(msg)
|
|
288
311
|
|
|
289
312
|
factory = _make_factory(
|
|
290
|
-
|
|
313
|
+
raw_to_derived_func=raw_to_derived,
|
|
291
314
|
set_derived=set_derived,
|
|
292
315
|
raw_devices_and_constants=raw_devices_and_constants,
|
|
293
316
|
)
|
|
@@ -343,3 +366,13 @@ def _partition_by_keys(data: dict, keys: set) -> tuple[dict, dict]:
|
|
|
343
366
|
else:
|
|
344
367
|
group_excluded[k] = v
|
|
345
368
|
return group_excluded, group_included
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _dict_wrapper(
|
|
372
|
+
fn: Callable[..., SignalDatatypeT],
|
|
373
|
+
) -> Callable[..., dict[str, SignalDatatypeT]]:
|
|
374
|
+
@functools.wraps(fn)
|
|
375
|
+
def wrapped(self, **kwargs):
|
|
376
|
+
return {"value": fn(**kwargs)}
|
|
377
|
+
|
|
378
|
+
return wrapped
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import types
|
|
3
4
|
from abc import abstractmethod
|
|
4
5
|
from collections.abc import Callable, Iterator, Sequence
|
|
5
6
|
from typing import (
|
|
@@ -9,8 +10,10 @@ from typing import (
|
|
|
9
10
|
NoReturn,
|
|
10
11
|
Protocol,
|
|
11
12
|
TypeVar,
|
|
13
|
+
Union,
|
|
12
14
|
cast,
|
|
13
15
|
get_args,
|
|
16
|
+
get_origin,
|
|
14
17
|
get_type_hints,
|
|
15
18
|
runtime_checkable,
|
|
16
19
|
)
|
|
@@ -76,6 +79,7 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
|
|
|
76
79
|
self._extras: dict[UniqueName, Sequence[Any]] = {}
|
|
77
80
|
self._signal_datatype: dict[LogicalName, type | None] = {}
|
|
78
81
|
self._vector_device_type: dict[LogicalName, type[Device] | None] = {}
|
|
82
|
+
self._optional_devices: set[str] = set()
|
|
79
83
|
self.ignored_signals: set[str] = set()
|
|
80
84
|
# Backends and Connectors stored ready for the connection phase
|
|
81
85
|
self._unfilled_backends: dict[
|
|
@@ -121,6 +125,20 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
|
|
|
121
125
|
self.ignored_signals.add(attr_name)
|
|
122
126
|
name = UniqueName(attr_name)
|
|
123
127
|
origin = get_origin_class(annotation)
|
|
128
|
+
args = get_args(annotation)
|
|
129
|
+
|
|
130
|
+
if (
|
|
131
|
+
get_origin(annotation) is Union
|
|
132
|
+
and types.NoneType in args
|
|
133
|
+
and len(args) == 2
|
|
134
|
+
):
|
|
135
|
+
# Annotation is an Union with two arguments, one of which is None
|
|
136
|
+
# Make this signal an optional parameter and set origin to T
|
|
137
|
+
# so the device is added to unfilled_connectors
|
|
138
|
+
self._optional_devices.add(name)
|
|
139
|
+
(annotation,) = [x for x in args if x is not types.NoneType]
|
|
140
|
+
origin = get_origin_class(annotation)
|
|
141
|
+
|
|
124
142
|
if (
|
|
125
143
|
name == "parent"
|
|
126
144
|
or name.startswith("_")
|
|
@@ -241,10 +259,17 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
|
|
|
241
259
|
:param source: The source of the data that should have done the filling, for
|
|
242
260
|
reporting as an error message
|
|
243
261
|
"""
|
|
244
|
-
unfilled =
|
|
245
|
-
|
|
262
|
+
unfilled = set(self._unfilled_connectors).union(self._unfilled_backends)
|
|
263
|
+
unfilled_optional = sorted(unfilled.intersection(self._optional_devices))
|
|
264
|
+
|
|
265
|
+
for name in unfilled_optional:
|
|
266
|
+
setattr(self._device, name, None)
|
|
267
|
+
|
|
268
|
+
required = sorted(unfilled.difference(unfilled_optional))
|
|
269
|
+
|
|
270
|
+
if required:
|
|
246
271
|
raise RuntimeError(
|
|
247
|
-
f"{self._device.name}: cannot provision {
|
|
272
|
+
f"{self._device.name}: cannot provision {required} from {source}"
|
|
248
273
|
)
|
|
249
274
|
|
|
250
275
|
def _ensure_device_vector(self, name: LogicalName) -> DeviceVector:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.2
|
|
4
4
|
Summary: Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
5
5
|
Author-email: Tom Cobb <tom.cobb@diamond.ac.uk>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
ophyd_async/__init__.py,sha256=dcAA3qsj1nNIMe5l-v2tlduZ_ypwBmyuHe45Lsq4k4w,206
|
|
2
2
|
ophyd_async/__main__.py,sha256=n_U4O9bgm97OuboUB_9eK7eFiwy8BZSgXJ0OzbE0DqU,481
|
|
3
3
|
ophyd_async/_docs_parser.py,sha256=gPYrigfSbYCF7QoSf2UvE-cpQu4snSssl7ZWN-kKDzI,352
|
|
4
|
-
ophyd_async/_version.py,sha256=
|
|
4
|
+
ophyd_async/_version.py,sha256=q0j1EdwWPylm9K8EEqyfgP0MBcuxBTAPKVxhFXx4oI8,706
|
|
5
5
|
ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
ophyd_async/core/__init__.py,sha256=GJxLNTxXD3Qlghq6CNe-mCeludgBBpHkHXkbDFojdHQ,6084
|
|
7
|
-
ophyd_async/core/_derived_signal.py,sha256=
|
|
7
|
+
ophyd_async/core/_derived_signal.py,sha256=9eJw_gNgr0mswrhbN7vK3hwwfXVzwpkumK7Ufxyhvds,14078
|
|
8
8
|
ophyd_async/core/_derived_signal_backend.py,sha256=Ibce9JHghiI5Ir8w0pUYULHL2qWkobeUYc0-CDrsO2E,12615
|
|
9
9
|
ophyd_async/core/_detector.py,sha256=9fYbBPmRnMGADcDTYkspDAL2uzhtNNiKCEeBUU0oKaY,14942
|
|
10
10
|
ophyd_async/core/_device.py,sha256=tm-khZLMy-Q7nn86GYHkbXRJOc83DgkbrdQ6WCjZhUs,17638
|
|
11
|
-
ophyd_async/core/_device_filler.py,sha256=
|
|
11
|
+
ophyd_async/core/_device_filler.py,sha256=6fj439jT0_INBe95_VBBdtwIx13mFDASjG07flhq7h8,15745
|
|
12
12
|
ophyd_async/core/_enums.py,sha256=2vh6x0rZ6SLiw2xxq1xVIn-GpbLDFc8wZoVdA55QiE8,370
|
|
13
13
|
ophyd_async/core/_flyer.py,sha256=8zKyU5aQOr_t59GIUwsYeb8NSabdvBp0swwuRe4v5VQ,3457
|
|
14
14
|
ophyd_async/core/_hdf_dataset.py,sha256=0bIX_ZbFSMdXqDwRtEvV-0avHnwXhjPddE5GVNmo7H8,2608
|
|
@@ -154,8 +154,8 @@ ophyd_async/testing/_one_of_everything.py,sha256=U9ui7B-iNHDM3H3hIWUuaCb8Gc2eLlU
|
|
|
154
154
|
ophyd_async/testing/_single_derived.py,sha256=5-HOTzgePcZ354NK_ssVpyIbJoJmKyjVQCxSwQXUC-4,2730
|
|
155
155
|
ophyd_async/testing/_utils.py,sha256=zClRo5ve8RGia7wQnby41W-Zprj-slOA5da1LfYnuhw,45
|
|
156
156
|
ophyd_async/testing/_wait_for_pending.py,sha256=YZAR48n-CW0GsPey3zFRzMJ4byDAr3HvMIoawjmTrHw,732
|
|
157
|
-
ophyd_async-0.14.
|
|
158
|
-
ophyd_async-0.14.
|
|
159
|
-
ophyd_async-0.14.
|
|
160
|
-
ophyd_async-0.14.
|
|
161
|
-
ophyd_async-0.14.
|
|
157
|
+
ophyd_async-0.14.2.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
|
|
158
|
+
ophyd_async-0.14.2.dist-info/METADATA,sha256=vbWuD-k0gWAz7cp4386pSDtBDzBlU_c2wXer3MzmltU,5703
|
|
159
|
+
ophyd_async-0.14.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
160
|
+
ophyd_async-0.14.2.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
|
|
161
|
+
ophyd_async-0.14.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|