ophyd-async 0.9.0a1__py3-none-any.whl → 0.10.0a1__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/__init__.py +5 -8
- ophyd_async/_docs_parser.py +12 -0
- ophyd_async/_version.py +9 -4
- ophyd_async/core/__init__.py +102 -74
- ophyd_async/core/_derived_signal.py +271 -0
- ophyd_async/core/_derived_signal_backend.py +300 -0
- ophyd_async/core/_detector.py +158 -153
- ophyd_async/core/_device.py +143 -115
- ophyd_async/core/_device_filler.py +82 -9
- ophyd_async/core/_flyer.py +16 -7
- ophyd_async/core/_hdf_dataset.py +29 -22
- ophyd_async/core/_log.py +14 -23
- ophyd_async/core/_mock_signal_backend.py +11 -3
- ophyd_async/core/_protocol.py +65 -45
- ophyd_async/core/_providers.py +28 -9
- ophyd_async/core/_readable.py +74 -58
- ophyd_async/core/_settings.py +113 -0
- ophyd_async/core/_signal.py +304 -174
- ophyd_async/core/_signal_backend.py +60 -14
- ophyd_async/core/_soft_signal_backend.py +18 -12
- ophyd_async/core/_status.py +72 -24
- ophyd_async/core/_table.py +54 -17
- ophyd_async/core/_utils.py +101 -52
- ophyd_async/core/_yaml_settings.py +66 -0
- ophyd_async/epics/__init__.py +1 -0
- ophyd_async/epics/adandor/__init__.py +9 -0
- ophyd_async/epics/adandor/_andor.py +45 -0
- ophyd_async/epics/adandor/_andor_controller.py +51 -0
- ophyd_async/epics/adandor/_andor_io.py +34 -0
- ophyd_async/epics/adaravis/__init__.py +8 -1
- ophyd_async/epics/adaravis/_aravis.py +23 -41
- ophyd_async/epics/adaravis/_aravis_controller.py +23 -55
- ophyd_async/epics/adaravis/_aravis_io.py +13 -28
- ophyd_async/epics/adcore/__init__.py +36 -14
- ophyd_async/epics/adcore/_core_detector.py +81 -0
- ophyd_async/epics/adcore/_core_io.py +145 -95
- ophyd_async/epics/adcore/_core_logic.py +179 -88
- ophyd_async/epics/adcore/_core_writer.py +223 -0
- ophyd_async/epics/adcore/_hdf_writer.py +51 -92
- ophyd_async/epics/adcore/_jpeg_writer.py +26 -0
- ophyd_async/epics/adcore/_single_trigger.py +6 -5
- ophyd_async/epics/adcore/_tiff_writer.py +26 -0
- ophyd_async/epics/adcore/_utils.py +3 -2
- ophyd_async/epics/adkinetix/__init__.py +2 -1
- ophyd_async/epics/adkinetix/_kinetix.py +32 -27
- ophyd_async/epics/adkinetix/_kinetix_controller.py +11 -21
- ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
- ophyd_async/epics/adpilatus/__init__.py +7 -2
- ophyd_async/epics/adpilatus/_pilatus.py +28 -40
- ophyd_async/epics/adpilatus/_pilatus_controller.py +25 -22
- ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
- ophyd_async/epics/adsimdetector/__init__.py +8 -1
- ophyd_async/epics/adsimdetector/_sim.py +22 -16
- ophyd_async/epics/adsimdetector/_sim_controller.py +9 -43
- ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
- ophyd_async/epics/advimba/__init__.py +10 -1
- ophyd_async/epics/advimba/_vimba.py +26 -25
- ophyd_async/epics/advimba/_vimba_controller.py +12 -24
- ophyd_async/epics/advimba/_vimba_io.py +23 -28
- ophyd_async/epics/core/_aioca.py +66 -30
- ophyd_async/epics/core/_epics_connector.py +4 -0
- ophyd_async/epics/core/_epics_device.py +2 -0
- ophyd_async/epics/core/_p4p.py +50 -18
- ophyd_async/epics/core/_pvi_connector.py +65 -8
- ophyd_async/epics/core/_signal.py +51 -51
- ophyd_async/epics/core/_util.py +5 -5
- ophyd_async/epics/demo/__init__.py +11 -49
- ophyd_async/epics/demo/__main__.py +31 -0
- ophyd_async/epics/demo/_ioc.py +32 -0
- ophyd_async/epics/demo/_motor.py +82 -0
- ophyd_async/epics/demo/_point_detector.py +42 -0
- ophyd_async/epics/demo/_point_detector_channel.py +22 -0
- ophyd_async/epics/demo/_stage.py +15 -0
- ophyd_async/epics/demo/{mover.db → motor.db} +2 -1
- ophyd_async/epics/demo/point_detector.db +59 -0
- ophyd_async/epics/demo/point_detector_channel.db +21 -0
- ophyd_async/epics/eiger/_eiger.py +1 -3
- ophyd_async/epics/eiger/_eiger_controller.py +11 -4
- ophyd_async/epics/eiger/_eiger_io.py +2 -0
- ophyd_async/epics/eiger/_odin_io.py +1 -2
- ophyd_async/epics/motor.py +83 -38
- ophyd_async/epics/signal.py +4 -1
- ophyd_async/epics/testing/__init__.py +14 -14
- ophyd_async/epics/testing/_example_ioc.py +68 -73
- ophyd_async/epics/testing/_utils.py +19 -44
- ophyd_async/epics/testing/test_records.db +16 -0
- ophyd_async/epics/testing/test_records_pva.db +17 -16
- ophyd_async/fastcs/__init__.py +1 -0
- ophyd_async/fastcs/core.py +6 -0
- ophyd_async/fastcs/odin/__init__.py +1 -0
- ophyd_async/fastcs/panda/__init__.py +8 -8
- ophyd_async/fastcs/panda/_block.py +29 -9
- ophyd_async/fastcs/panda/_control.py +12 -2
- ophyd_async/fastcs/panda/_hdf_panda.py +5 -1
- ophyd_async/fastcs/panda/_table.py +13 -7
- ophyd_async/fastcs/panda/_trigger.py +23 -9
- ophyd_async/fastcs/panda/_writer.py +27 -30
- ophyd_async/plan_stubs/__init__.py +16 -0
- ophyd_async/plan_stubs/_ensure_connected.py +12 -17
- ophyd_async/plan_stubs/_fly.py +3 -5
- ophyd_async/plan_stubs/_nd_attributes.py +9 -5
- ophyd_async/plan_stubs/_panda.py +14 -0
- ophyd_async/plan_stubs/_settings.py +152 -0
- ophyd_async/plan_stubs/_utils.py +3 -0
- ophyd_async/plan_stubs/_wait_for_awaitable.py +13 -0
- ophyd_async/sim/__init__.py +29 -0
- ophyd_async/sim/__main__.py +43 -0
- ophyd_async/sim/_blob_detector.py +33 -0
- ophyd_async/sim/_blob_detector_controller.py +48 -0
- ophyd_async/sim/_blob_detector_writer.py +105 -0
- ophyd_async/sim/_mirror_horizontal.py +46 -0
- ophyd_async/sim/_mirror_vertical.py +74 -0
- ophyd_async/sim/_motor.py +233 -0
- ophyd_async/sim/_pattern_generator.py +124 -0
- ophyd_async/sim/_point_detector.py +86 -0
- ophyd_async/sim/_stage.py +19 -0
- ophyd_async/tango/__init__.py +1 -0
- ophyd_async/tango/core/__init__.py +6 -1
- ophyd_async/tango/core/_base_device.py +41 -33
- ophyd_async/tango/core/_converters.py +81 -0
- ophyd_async/tango/core/_signal.py +21 -33
- ophyd_async/tango/core/_tango_readable.py +2 -19
- ophyd_async/tango/core/_tango_transport.py +148 -74
- ophyd_async/tango/core/_utils.py +47 -0
- ophyd_async/tango/demo/_counter.py +2 -0
- ophyd_async/tango/demo/_detector.py +2 -0
- ophyd_async/tango/demo/_mover.py +10 -6
- ophyd_async/tango/demo/_tango/_servers.py +4 -0
- ophyd_async/tango/testing/__init__.py +6 -0
- ophyd_async/tango/testing/_one_of_everything.py +200 -0
- ophyd_async/testing/__init__.py +48 -7
- ophyd_async/testing/__pytest_assert_rewrite.py +4 -0
- ophyd_async/testing/_assert.py +200 -96
- ophyd_async/testing/_mock_signal_utils.py +59 -73
- ophyd_async/testing/_one_of_everything.py +146 -0
- ophyd_async/testing/_single_derived.py +87 -0
- ophyd_async/testing/_utils.py +3 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/METADATA +25 -26
- ophyd_async-0.10.0a1.dist-info/RECORD +149 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/WHEEL +1 -1
- ophyd_async/core/_device_save_loader.py +0 -274
- ophyd_async/epics/demo/_mover.py +0 -95
- ophyd_async/epics/demo/_sensor.py +0 -37
- ophyd_async/epics/demo/sensor.db +0 -19
- ophyd_async/fastcs/panda/_utils.py +0 -16
- ophyd_async/sim/demo/__init__.py +0 -19
- ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -13
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -42
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +0 -62
- ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -41
- ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -207
- ophyd_async/sim/demo/_sim_motor.py +0 -107
- ophyd_async/sim/testing/__init__.py +0 -0
- ophyd_async-0.9.0a1.dist-info/RECORD +0 -119
- ophyd_async-0.9.0a1.dist-info/entry_points.txt +0 -2
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info/licenses}/LICENSE +0 -0
- {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/top_level.txt +0 -0
ophyd_async/core/_readable.py
CHANGED
|
@@ -17,25 +17,34 @@ from ._utils import merge_gathered_dicts
|
|
|
17
17
|
class StandardReadableFormat(Enum):
|
|
18
18
|
"""Declare how a `Device` should contribute to the `StandardReadable` verbs."""
|
|
19
19
|
|
|
20
|
-
#: Detect which verbs the child supports and contribute to:
|
|
21
|
-
#:
|
|
22
|
-
#: - ``read()``, ``describe()`` if it is `bluesky.protocols.Readable`
|
|
23
|
-
#: - ``read_configuration()``, ``describe_configuration()`` if it is
|
|
24
|
-
#: `bluesky.protocols.Configurable`
|
|
25
|
-
#: - ``stage()``, ``unstage()`` if it is `bluesky.protocols.Stageable`
|
|
26
|
-
#: - ``hints`` if it `bluesky.protocols.HasHints`
|
|
27
20
|
CHILD = "CHILD"
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
"""Detect which verbs the child supports and contribute to:
|
|
22
|
+
|
|
23
|
+
- `read()`, `describe()` if it is [](#bluesky.protocols.Readable)
|
|
24
|
+
- `read_configuration()`, `describe_configuration()` if it is
|
|
25
|
+
[](#bluesky.protocols.Configurable)
|
|
26
|
+
- `stage()`, `unstage()` if it is [](#bluesky.protocols.Stageable)
|
|
27
|
+
- `hints` if it [](#bluesky.protocols.HasHints)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
30
|
CONFIG_SIGNAL = "CONFIG_SIGNAL"
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
"""Contribute the `Signal` value to `read_configuration()` and
|
|
32
|
+
`describe_configuration()`
|
|
33
|
+
"""
|
|
34
|
+
|
|
33
35
|
HINTED_SIGNAL = "HINTED_SIGNAL"
|
|
34
|
-
|
|
36
|
+
"""Contribute the monitored `Signal` value to `read()` and `describe()` and
|
|
37
|
+
put the signal name in `hints`
|
|
38
|
+
"""
|
|
39
|
+
|
|
35
40
|
UNCACHED_SIGNAL = "UNCACHED_SIGNAL"
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
"""Contribute the uncached `Signal` value to `read()` and `describe()`
|
|
42
|
+
"""
|
|
43
|
+
|
|
38
44
|
HINTED_UNCACHED_SIGNAL = "HINTED_UNCACHED_SIGNAL"
|
|
45
|
+
"""Contribute the uncached `Signal` value to `read()` and `describe()` and
|
|
46
|
+
put the signal name in `hints`
|
|
47
|
+
"""
|
|
39
48
|
|
|
40
49
|
def __call__(self, parent: Device, child: Device):
|
|
41
50
|
if not isinstance(parent, StandardReadable):
|
|
@@ -74,11 +83,15 @@ HintedSignal.uncached = _compat_format(
|
|
|
74
83
|
class StandardReadable(
|
|
75
84
|
Device, AsyncReadable, AsyncConfigurable, AsyncStageable, HasHints
|
|
76
85
|
):
|
|
77
|
-
"""Device that
|
|
86
|
+
"""Device that provides selected child Device values in `read()`.
|
|
87
|
+
|
|
88
|
+
Provides the ability for children to be registered to:
|
|
89
|
+
- Participate in `stage()` and `unstage()`
|
|
90
|
+
- Provide their value in `read()` and `describe()
|
|
91
|
+
- Provide their value in `read_configuration()` and `describe_configuration()
|
|
92
|
+
- Select a value to appear in `hints`
|
|
78
93
|
|
|
79
|
-
|
|
80
|
-
- Signals can be registered for read() and read_configuration()
|
|
81
|
-
- These signals will be subscribed for read() between stage() and unstage()
|
|
94
|
+
The behavior is customized with a [](#StandardReadableFormat)
|
|
82
95
|
"""
|
|
83
96
|
|
|
84
97
|
# These must be immutable types to avoid accidental sharing between
|
|
@@ -119,33 +132,35 @@ class StandardReadable(
|
|
|
119
132
|
hints: Hints = {}
|
|
120
133
|
for new_hint in self._has_hints:
|
|
121
134
|
# Merge the existing and new hints, based on the type of the value.
|
|
122
|
-
# This avoids default dict merge
|
|
135
|
+
# This avoids default dict merge behavior that overrides the values;
|
|
123
136
|
# we want to combine them when they are Sequences, and ensure they are
|
|
124
137
|
# identical when string values.
|
|
125
138
|
for key, value in new_hint.hints.items():
|
|
139
|
+
# fail early for unkwon types
|
|
126
140
|
if isinstance(value, str):
|
|
127
141
|
if key in hints:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
if hints[key] != value:
|
|
143
|
+
msg = f"Hints key {key} value may not be overridden"
|
|
144
|
+
raise RuntimeError(msg)
|
|
131
145
|
else:
|
|
132
146
|
hints[key] = value # type: ignore[literal-required]
|
|
133
147
|
elif isinstance(value, Sequence):
|
|
134
148
|
if key in hints:
|
|
135
149
|
for new_val in value:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
if new_val in hints[key]:
|
|
151
|
+
msg = f"Hint {key} {new_val} overrides existing hint"
|
|
152
|
+
raise RuntimeError(msg)
|
|
139
153
|
hints[key] = ( # type: ignore[literal-required]
|
|
140
154
|
hints[key] + value # type: ignore[literal-required]
|
|
141
155
|
)
|
|
142
156
|
else:
|
|
143
157
|
hints[key] = value # type: ignore[literal-required]
|
|
144
158
|
else:
|
|
145
|
-
|
|
146
|
-
f"{new_hint.name}: Unknown type for value '{value}'
|
|
159
|
+
msg = (
|
|
160
|
+
f"{new_hint.name}: Unknown type for value '{value}'"
|
|
147
161
|
f" for key '{key}'"
|
|
148
162
|
)
|
|
163
|
+
raise TypeError(msg)
|
|
149
164
|
|
|
150
165
|
return hints
|
|
151
166
|
|
|
@@ -154,13 +169,12 @@ class StandardReadable(
|
|
|
154
169
|
self,
|
|
155
170
|
format: StandardReadableFormat = StandardReadableFormat.CHILD,
|
|
156
171
|
) -> Generator[None, None, None]:
|
|
157
|
-
"""Context manager that calls
|
|
172
|
+
"""Context manager that calls [](#add_readables) on child Devices added within.
|
|
158
173
|
|
|
159
|
-
Scans
|
|
160
|
-
`add_readables` on any that are added with the provided
|
|
174
|
+
Scans `self.children()` on entry and exit to context manager, and calls
|
|
175
|
+
`add_readables()` on any that are added with the provided
|
|
161
176
|
`StandardReadableFormat`.
|
|
162
177
|
"""
|
|
163
|
-
|
|
164
178
|
dict_copy = dict(self.children())
|
|
165
179
|
|
|
166
180
|
yield
|
|
@@ -190,20 +204,22 @@ class StandardReadable(
|
|
|
190
204
|
Use output from the given devices to contribute to the verbs of the following
|
|
191
205
|
interfaces:
|
|
192
206
|
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
format:
|
|
203
|
-
Determines which of the devices functions are added to which verb as per the
|
|
204
|
-
`StandardReadableFormat` documentation
|
|
207
|
+
- [](#bluesky.protocols.Readable)
|
|
208
|
+
- [](#bluesky.protocols.Configurable)
|
|
209
|
+
- [](#bluesky.protocols.Stageable)
|
|
210
|
+
- [](#bluesky.protocols.HasHints)
|
|
211
|
+
|
|
212
|
+
:param devices: The devices to be added
|
|
213
|
+
:param format:
|
|
214
|
+
Determines which of the devices functions are added to which verb as
|
|
215
|
+
per the [](#StandardReadableFormat) documentation
|
|
205
216
|
"""
|
|
206
217
|
|
|
218
|
+
def assert_device_is_signalr(device: Device) -> SignalR:
|
|
219
|
+
if not isinstance(device, SignalR):
|
|
220
|
+
raise TypeError(f"{device} is not a SignalR")
|
|
221
|
+
return device
|
|
222
|
+
|
|
207
223
|
for device in devices:
|
|
208
224
|
match format:
|
|
209
225
|
case StandardReadableFormat.CHILD:
|
|
@@ -218,24 +234,24 @@ class StandardReadable(
|
|
|
218
234
|
if isinstance(device, HasHints):
|
|
219
235
|
self._has_hints += (device,)
|
|
220
236
|
case StandardReadableFormat.CONFIG_SIGNAL:
|
|
221
|
-
|
|
222
|
-
self._describe_config_funcs += (
|
|
223
|
-
self._read_config_funcs += (
|
|
237
|
+
signalr_device = assert_device_is_signalr(device=device)
|
|
238
|
+
self._describe_config_funcs += (signalr_device.describe,)
|
|
239
|
+
self._read_config_funcs += (signalr_device.read,)
|
|
224
240
|
case StandardReadableFormat.HINTED_SIGNAL:
|
|
225
|
-
|
|
226
|
-
self._describe_funcs += (
|
|
227
|
-
self._read_funcs += (
|
|
228
|
-
self._stageables += (
|
|
229
|
-
self._has_hints += (_HintsFromName(
|
|
241
|
+
signalr_device = assert_device_is_signalr(device=device)
|
|
242
|
+
self._describe_funcs += (signalr_device.describe,)
|
|
243
|
+
self._read_funcs += (signalr_device.read,)
|
|
244
|
+
self._stageables += (signalr_device,)
|
|
245
|
+
self._has_hints += (_HintsFromName(signalr_device),)
|
|
230
246
|
case StandardReadableFormat.UNCACHED_SIGNAL:
|
|
231
|
-
|
|
232
|
-
self._describe_funcs += (
|
|
233
|
-
self._read_funcs += (_UncachedRead(
|
|
247
|
+
signalr_device = assert_device_is_signalr(device=device)
|
|
248
|
+
self._describe_funcs += (signalr_device.describe,)
|
|
249
|
+
self._read_funcs += (_UncachedRead(signalr_device),)
|
|
234
250
|
case StandardReadableFormat.HINTED_UNCACHED_SIGNAL:
|
|
235
|
-
|
|
236
|
-
self._describe_funcs += (
|
|
237
|
-
self._read_funcs += (_UncachedRead(
|
|
238
|
-
self._has_hints += (_HintsFromName(
|
|
251
|
+
signalr_device = assert_device_is_signalr(device=device)
|
|
252
|
+
self._describe_funcs += (signalr_device.describe,)
|
|
253
|
+
self._read_funcs += (_UncachedRead(signalr_device),)
|
|
254
|
+
self._has_hints += (_HintsFromName(signalr_device),)
|
|
239
255
|
|
|
240
256
|
|
|
241
257
|
class _UncachedRead:
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from collections.abc import Callable, Iterator, MutableMapping
|
|
5
|
+
from typing import Any, Generic
|
|
6
|
+
|
|
7
|
+
from ._device import Device, DeviceT
|
|
8
|
+
from ._signal import SignalRW
|
|
9
|
+
from ._signal_backend import SignalDatatypeT
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Settings(MutableMapping[SignalRW[Any], Any], Generic[DeviceT]):
|
|
13
|
+
"""Used for supplying settings to signals.
|
|
14
|
+
|
|
15
|
+
:param device: The device that the settings are for.
|
|
16
|
+
:param settings: A dictionary of settings to start with.
|
|
17
|
+
|
|
18
|
+
:example:
|
|
19
|
+
```python
|
|
20
|
+
# Settings are created from a dict of signals to values
|
|
21
|
+
settings1 = Settings(device, {device.sig1: 1, device.sig2: 2})
|
|
22
|
+
settings2 = Settings(device, {device.sig1: 10, device.sig3: 3})
|
|
23
|
+
# They act like a dictionaries
|
|
24
|
+
assert settings1[device.sig1] == 1
|
|
25
|
+
# Including the ability to "or" two settings together
|
|
26
|
+
settings = settings1 | settings2
|
|
27
|
+
assert dict(settings) == {
|
|
28
|
+
device.sig1: 10,
|
|
29
|
+
device.sig2: 2,
|
|
30
|
+
device.sig3: 3,
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self, device: DeviceT, settings: MutableMapping[SignalRW, Any] | None = None
|
|
37
|
+
):
|
|
38
|
+
self.device = device
|
|
39
|
+
self._settings = {}
|
|
40
|
+
self.update(settings or {})
|
|
41
|
+
|
|
42
|
+
def __getitem__(self, key: SignalRW[SignalDatatypeT]) -> SignalDatatypeT:
|
|
43
|
+
return self._settings[key]
|
|
44
|
+
|
|
45
|
+
def _is_in_device(self, device: Device) -> bool:
|
|
46
|
+
while device.parent and device.parent is not self.device:
|
|
47
|
+
# While we have a parent that is not the right device
|
|
48
|
+
# continue searching up the tree
|
|
49
|
+
device = device.parent
|
|
50
|
+
return device.parent is self.device
|
|
51
|
+
|
|
52
|
+
def __setitem__(
|
|
53
|
+
self, key: SignalRW[SignalDatatypeT], value: SignalDatatypeT | None
|
|
54
|
+
) -> None:
|
|
55
|
+
# Check the types on entry to dict to make sure we can't accidentally
|
|
56
|
+
# add a non-signal type
|
|
57
|
+
if not isinstance(key, SignalRW):
|
|
58
|
+
raise TypeError(f"Expected SignalRW, got {key}")
|
|
59
|
+
if not self._is_in_device(key):
|
|
60
|
+
raise KeyError(f"Signal {key} is not a child of {self.device}")
|
|
61
|
+
self._settings[key] = value
|
|
62
|
+
|
|
63
|
+
def __delitem__(self, key: SignalRW) -> None:
|
|
64
|
+
del self._settings[key]
|
|
65
|
+
|
|
66
|
+
def __iter__(self) -> Iterator[SignalRW]:
|
|
67
|
+
yield from iter(self._settings)
|
|
68
|
+
|
|
69
|
+
def __len__(self) -> int:
|
|
70
|
+
return len(self._settings)
|
|
71
|
+
|
|
72
|
+
def __or__(self, other: MutableMapping[SignalRW, Any]) -> Settings[DeviceT]:
|
|
73
|
+
"""Create a new Settings that is the union of self overridden by other."""
|
|
74
|
+
if isinstance(other, Settings) and not self._is_in_device(other.device):
|
|
75
|
+
raise ValueError(f"{other.device} is not a child of {self.device}")
|
|
76
|
+
return Settings(self.device, self._settings | dict(other))
|
|
77
|
+
|
|
78
|
+
def partition(
|
|
79
|
+
self, predicate: Callable[[SignalRW], bool]
|
|
80
|
+
) -> tuple[Settings[DeviceT], Settings[DeviceT]]:
|
|
81
|
+
"""Partition into two Settings based on a predicate.
|
|
82
|
+
|
|
83
|
+
:param predicate:
|
|
84
|
+
Callable that takes each signal, and returns a boolean to say if it
|
|
85
|
+
should be in the first returned Settings
|
|
86
|
+
:returns:
|
|
87
|
+
`(where_true, where_false)` where each is a Settings object.
|
|
88
|
+
The first contains the signals for which the predicate returned True,
|
|
89
|
+
and the second contains the signals for which the predicate returned False.
|
|
90
|
+
|
|
91
|
+
:example:
|
|
92
|
+
```python
|
|
93
|
+
settings = Settings(device, {device.special: 1, device.sig: 2})
|
|
94
|
+
specials, others = settings.partition(lambda sig: "special" in sig.name)
|
|
95
|
+
```
|
|
96
|
+
"""
|
|
97
|
+
where_true, where_false = Settings(self.device), Settings(self.device)
|
|
98
|
+
for signal, value in self.items():
|
|
99
|
+
dest = where_true if predicate(signal) else where_false
|
|
100
|
+
dest[signal] = value
|
|
101
|
+
return where_true, where_false
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class SettingsProvider:
|
|
105
|
+
"""Base class for providing settings."""
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
async def store(self, name: str, data: dict[str, Any]):
|
|
109
|
+
"""Store the data, associating it with the given name."""
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
async def retrieve(self, name: str) -> dict[str, Any]:
|
|
113
|
+
"""Retrieve the data associated with the given name."""
|