ophyd-async 0.10.0a4__py3-none-any.whl → 0.10.1__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 CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.10.0a4'
21
- __version_tuple__ = version_tuple = (0, 10, 0, 'a4')
20
+ __version__ = version = '0.10.1'
21
+ __version_tuple__ = version_tuple = (0, 10, 1)
@@ -248,7 +248,7 @@ def derived_signal_rw(
248
248
  if raw_to_derived_datatype != set_derived_datatype:
249
249
  msg = (
250
250
  f"{raw_to_derived} has datatype {raw_to_derived_datatype} "
251
- f"!= {set_derived_datatype} dataype {set_derived_datatype}"
251
+ f"!= {set_derived_datatype} datatype {set_derived_datatype}"
252
252
  )
253
253
  raise TypeError(msg)
254
254
 
@@ -61,7 +61,7 @@ class Transform(BaseModel, Generic[RawT, DerivedT]):
61
61
  TransformT = TypeVar("TransformT", bound=Transform)
62
62
 
63
63
 
64
- def filter_by_type(raw_devices: Mapping[str, Any], type_: type[T]) -> dict[str, T]:
64
+ def validate_by_type(raw_devices: Mapping[str, Any], type_: type[T]) -> dict[str, T]:
65
65
  filtered_devices: dict[str, T] = {}
66
66
  for name, device in raw_devices.items():
67
67
  if not isinstance(device, type_):
@@ -91,21 +91,23 @@ class SignalTransformer(Generic[TransformT]):
91
91
 
92
92
  @cached_property
93
93
  def raw_locatables(self) -> dict[str, AsyncLocatable]:
94
- return filter_by_type(self._raw_devices, AsyncLocatable)
94
+ return validate_by_type(self._raw_devices, AsyncLocatable)
95
95
 
96
96
  @cached_property
97
97
  def transform_readables(self) -> dict[str, AsyncReadable]:
98
- return filter_by_type(self._transform_devices, AsyncReadable)
98
+ return validate_by_type(self._transform_devices, AsyncReadable)
99
99
 
100
100
  @cached_property
101
101
  def raw_and_transform_readables(self) -> dict[str, AsyncReadable]:
102
- return filter_by_type(
102
+ return validate_by_type(
103
103
  self._raw_devices | self._transform_devices, AsyncReadable
104
104
  )
105
105
 
106
106
  @cached_property
107
107
  def raw_and_transform_subscribables(self) -> dict[str, Subscribable]:
108
- return filter_by_type(self._raw_devices | self._transform_devices, Subscribable)
108
+ return validate_by_type(
109
+ self._raw_devices | self._transform_devices, Subscribable
110
+ )
109
111
 
110
112
  def _complete_cached_reading(self) -> dict[str, Reading] | None:
111
113
  if self._cached_readings and len(self._cached_readings) == len(
@@ -23,7 +23,7 @@ class HDFDatasetDescription(BaseModel):
23
23
  """The dataset name within the HDF file,
24
24
  e.g. /entry/data/data or /entry/instrument/NDAttributes/sum"""
25
25
 
26
- shape: tuple[int, ...] = Field(default_factory=tuple)
26
+ shape: tuple[int | None, ...] = Field(default_factory=tuple)
27
27
  """The shape of a single event's data in the HDF file,
28
28
  e.g. (1, 768, 1024) for arrays or () for scalars"""
29
29
 
@@ -228,5 +228,5 @@ class DatasetDescriber(Protocol):
228
228
  """Return the numpy datatype for this dataset."""
229
229
 
230
230
  @abstractmethod
231
- async def shape(self) -> tuple[int, ...]:
231
+ async def shape(self) -> tuple[int | None, ...]:
232
232
  """Get the shape of the data collection."""
@@ -163,7 +163,12 @@ class _SignalCache(Generic[SignalDatatypeT]):
163
163
  self._notify(function, want_value)
164
164
 
165
165
  def unsubscribe(self, function: Callback) -> bool:
166
- self._listeners.pop(function)
166
+ _listener = self._listeners.pop(function, None)
167
+ if not _listener:
168
+ self._signal.log.warning(
169
+ f"Unsubscribe failed: subscriber {function} was not found "
170
+ f" in listeners list: {list(self._listeners)}"
171
+ )
167
172
  return self._staged or bool(self._listeners)
168
173
 
169
174
  def set_staged(self, staged: bool) -> bool:
@@ -176,7 +176,7 @@ def _datakey_dtype_numpy(
176
176
  raise TypeError(f"Can't make dtype_numpy for {datatype}")
177
177
 
178
178
 
179
- def _datakey_shape(value: SignalDatatype) -> list[int]:
179
+ def _datakey_shape(value: SignalDatatype) -> list[int | None]:
180
180
  if type(value) in _primitive_dtype or isinstance(value, EnumTypes):
181
181
  return []
182
182
  elif isinstance(value, np.ndarray):
@@ -7,6 +7,7 @@ from ophyd_async.core import (
7
7
  DetectorController,
8
8
  DetectorTrigger,
9
9
  TriggerInfo,
10
+ observe_value,
10
11
  set_and_wait_for_value,
11
12
  )
12
13
 
@@ -89,34 +90,57 @@ class ADBaseController(DetectorController, Generic[ADBaseIOT]):
89
90
  self.driver.acquire_period.set(full_frame_time, timeout=timeout),
90
91
  )
91
92
 
92
- async def start_acquiring_driver_and_ensure_status(self) -> AsyncStatus:
93
+ async def start_acquiring_driver_and_ensure_status(
94
+ self,
95
+ start_timeout: float = DEFAULT_TIMEOUT,
96
+ state_timeout: float = DEFAULT_TIMEOUT,
97
+ ) -> AsyncStatus:
93
98
  """Start acquiring driver, raising ValueError if the detector is in a bad state.
94
99
 
95
100
  This sets driver.acquire to True, and waits for it to be True up to a timeout.
96
101
  Then, it checks that the DetectorState PV is in DEFAULT_GOOD_STATES,
97
102
  and otherwise raises a ValueError.
98
103
 
104
+
105
+ :param start_timeout:
106
+ Timeout used for waiting for the driver to start
107
+ acquiring.
108
+ :param state_timeout:
109
+ Timeout used for waiting for the detector to be in a good
110
+ state after it stops acquiring.
99
111
  :returns AsyncStatus:
100
112
  An AsyncStatus that can be awaited to set driver.acquire to True and perform
101
113
  subsequent raising (if applicable) due to detector state.
114
+
102
115
  """
103
116
  status = await set_and_wait_for_value(
104
117
  self.driver.acquire,
105
118
  True,
106
- timeout=DEFAULT_TIMEOUT,
119
+ timeout=start_timeout,
107
120
  wait_for_set_completion=False,
108
121
  )
109
122
 
110
123
  async def complete_acquisition() -> None:
111
- # NOTE: possible race condition here between the callback from
112
- # set_and_wait_for_value and the detector state updating.
113
124
  await status
114
- state = await self.driver.detector_state.get_value()
115
- if state not in self.good_states:
116
- raise ValueError(
117
- f"Final detector state {state.value} not "
118
- "in valid end states: {self.good_states}"
119
- )
125
+ state = None
126
+ try:
127
+ async for state in observe_value(
128
+ self.driver.detector_state, done_timeout=state_timeout
129
+ ):
130
+ if state in self.good_states:
131
+ return
132
+ except asyncio.TimeoutError as exc:
133
+ if state is not None:
134
+ raise ValueError(
135
+ f"Final detector state {state.value} not in valid end "
136
+ f"states: {self.good_states}"
137
+ ) from exc
138
+ else:
139
+ # No updates from the detector, something else is wrong
140
+ raise asyncio.TimeoutError(
141
+ "Could not monitor detector state: "
142
+ + self.driver.detector_state.source
143
+ ) from exc
120
144
 
121
145
  return AsyncStatus(complete_acquisition())
122
146
 
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  from collections.abc import AsyncIterator
3
3
  from pathlib import Path
4
+ from typing import TypeGuard
4
5
  from xml.etree import ElementTree as ET
5
6
 
6
7
  from bluesky.protocols import StreamAsset
@@ -22,6 +23,10 @@ from ._utils import (
22
23
  )
23
24
 
24
25
 
26
+ def _is_fully_described(shape: tuple[int | None, ...]) -> TypeGuard[tuple[int, ...]]:
27
+ return None not in shape
28
+
29
+
25
30
  class ADHDFWriter(ADWriter[NDFileHDFIO]):
26
31
  """Allow `NDFileHDFIO` to be used within `StandardDetector`."""
27
32
 
@@ -75,6 +80,16 @@ class ADHDFWriter(ADWriter[NDFileHDFIO]):
75
80
  # Determine number of frames that will be saved per HDF chunk
76
81
  frames_per_chunk = await self.fileio.num_frames_chunks.get_value()
77
82
 
83
+ if not _is_fully_described(detector_shape):
84
+ # Questions:
85
+ # - Can AreaDetector support this?
86
+ # - How to deal with chunking?
87
+ # Don't support for now - leave option open to support it later
88
+ raise ValueError(
89
+ "Datasets with partially unknown dimensionality "
90
+ "are not currently supported by ADHDFWriter."
91
+ )
92
+
78
93
  # Add the main data
79
94
  self._datasets = [
80
95
  HDFDatasetDescription(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ophyd-async
3
- Version: 0.10.0a4
3
+ Version: 0.10.1
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
@@ -43,7 +43,7 @@ Description-Content-Type: text/markdown
43
43
  License-File: LICENSE
44
44
  Requires-Dist: numpy
45
45
  Requires-Dist: bluesky>=1.13.1rc2
46
- Requires-Dist: event-model>=1.22.1
46
+ Requires-Dist: event-model>=1.23
47
47
  Requires-Dist: pyyaml
48
48
  Requires-Dist: colorlog
49
49
  Requires-Dist: pydantic>=2.0
@@ -1,24 +1,24 @@
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=nAYruEsg-pmbrsaMZ5jXkxpaKnALiE0J3VPjge9G13A,521
4
+ ophyd_async/_version.py,sha256=StiR6uxiq6hqMzT3MUIl_ZooIq2cetH9oWrHUI_qWFU,513
5
5
  ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  ophyd_async/core/__init__.py,sha256=mSoldXL3hou1UifwsUcos2Q3Vy_pAaNJJWdMc0VInyg,4516
7
- ophyd_async/core/_derived_signal.py,sha256=sSb-nRkcZzjyo2eG_z4Rawadkt0QPZTEWtcV3a_TTBM,11439
8
- ophyd_async/core/_derived_signal_backend.py,sha256=pci61vJkGnsMYxB54AZHyOr4rJECRpigNFtUZTM0P20,12211
7
+ ophyd_async/core/_derived_signal.py,sha256=7ZZrDUnS2DGVwJZNX6_u0u-lAHKHCC7bL5auIfYV9w8,11440
8
+ ophyd_async/core/_derived_signal_backend.py,sha256=AZpyMmt3-ssymtDIa3138qPp1srmaId137A_yzQuFD0,12243
9
9
  ophyd_async/core/_detector.py,sha256=PbWpv7lyBvYEJV-prG9qU0DadmxhyzMhQ9oLC0N7vwQ,15193
10
10
  ophyd_async/core/_device.py,sha256=QSOgjXkvBpPWE1VhQ21tIC8d9z4Oadr6C_1tk4J21W4,14617
11
11
  ophyd_async/core/_device_filler.py,sha256=MDz8eQQ-eEAwo-UEMxfqPfpcBuMG01tLCGR6utwVnmE,14825
12
12
  ophyd_async/core/_flyer.py,sha256=YPCGAokF0GofOOO2OLrsSLoZSXs_RzmmrNglBunA31o,1924
13
- ophyd_async/core/_hdf_dataset.py,sha256=jOYcrJUvZy9VarHCxs-eOIYME5u1yGOZK9CqccCEA6E,2868
13
+ ophyd_async/core/_hdf_dataset.py,sha256=MKgA-6uxtCg-cxpGKAIxdWOw55CZp66si8lsak5e9CA,2875
14
14
  ophyd_async/core/_log.py,sha256=DxKR4Nz3SgTaTzKBZWqt-w48yT8WUAr_3Qr223TEWRw,3587
15
15
  ophyd_async/core/_mock_signal_backend.py,sha256=SPdCbVWss6-iL9C3t9u0IvR_Ln9JeDypVd18WlivdjE,3156
16
16
  ophyd_async/core/_protocol.py,sha256=wQ_snxhTprHqEjQb1HgFwBljwolMY6A8C3xgV1PXwdU,4051
17
- ophyd_async/core/_providers.py,sha256=0R--Slh_E16l4AQoNtessQZpOwIn7uBowvcnzh8jYgk,7538
17
+ ophyd_async/core/_providers.py,sha256=1XuLUw9sT1pKMfH_PsDEpIi1gulla7NfPSp3IR3KfEA,7545
18
18
  ophyd_async/core/_readable.py,sha256=iBo1YwA5bsAbzLbznvmSnzKDWUuGkLh850Br3BXsgeU,11707
19
19
  ophyd_async/core/_settings.py,sha256=_ZccbXKP7j5rG6-bMKk7aaLr8hChdRDAPY_YSR71XXM,4213
20
- ophyd_async/core/_signal.py,sha256=isPya2iextwRcWAV1SOf7CUwyL0PJloAUVH8rp1G2tE,27134
21
- ophyd_async/core/_signal_backend.py,sha256=9MIzT3ArlL7BJlmWQxr4Moxc4LGQhmTclfCQfIlq4Ps,6923
20
+ ophyd_async/core/_signal.py,sha256=1XHhnYwXa0yusSKdaRAiPUoVA5atcWd0S1ztBSaEi9k,27369
21
+ ophyd_async/core/_signal_backend.py,sha256=PvwTbbSVEGqM-2s5BNRrKGwM_MiYL71qMxYAgyZ7wRM,6930
22
22
  ophyd_async/core/_soft_signal_backend.py,sha256=zrE7H2ojHY6oQBucLkFgukszrkdvbIZuavLjEUqc_xM,6227
23
23
  ophyd_async/core/_status.py,sha256=h4TtWFM7wFtpxxyAYYSITgcVzArYZdYBHbya6qIX5t0,6553
24
24
  ophyd_async/core/_table.py,sha256=l5GzL3La68ZkXpcOOVntnYr8ofFV8kWGr1hzY1ek83w,6897
@@ -38,9 +38,9 @@ ophyd_async/epics/adaravis/_aravis_io.py,sha256=af5RxeXF2ligvAXwMNMKHA4QHTR_WmNF
38
38
  ophyd_async/epics/adcore/__init__.py,sha256=GipuBZwaAju4g15WjvGs78S4zjGVxmbPel4E29zHFvE,1583
39
39
  ophyd_async/epics/adcore/_core_detector.py,sha256=mRDaHgXCTZF-MIVsU1csoQx9jObutYDpMWayugx2-jI,2631
40
40
  ophyd_async/epics/adcore/_core_io.py,sha256=jm4gQUSq727xnPriuH6jFHqlDBZceKxr_XyBNj5F65U,7392
41
- ophyd_async/epics/adcore/_core_logic.py,sha256=CjzrfC5HiwoLI1igqRKIn2SSH86T_QmAFi1-kEdyGyM,8075
41
+ ophyd_async/epics/adcore/_core_logic.py,sha256=IH7iOSVIsVj4e97ClhdBFpmdMkb8TznSaLkd3ohEhUs,8884
42
42
  ophyd_async/epics/adcore/_core_writer.py,sha256=S58plRmbNzig-r7gubKjDVDni_nf4CASaV1OJvudbHw,8019
43
- ophyd_async/epics/adcore/_hdf_writer.py,sha256=ns7T9tb54ReGnyYs0fITg3Cb_0DSz4EDKlKtjBkmG3k,5572
43
+ ophyd_async/epics/adcore/_hdf_writer.py,sha256=GW_3NKRTF3kc9Wwz6vf5a7eqlnl81LXgk8afLgZaeNU,6134
44
44
  ophyd_async/epics/adcore/_jpeg_writer.py,sha256=7XC4Twx_MaCBjeol27UA-hStOCQEjkEAb3ToVMPUlZ8,670
45
45
  ophyd_async/epics/adcore/_single_trigger.py,sha256=tFGLT1b_rZzAvbqWP-hyCccxJMRY26T5IER-VAqKXmc,1275
46
46
  ophyd_async/epics/adcore/_tiff_writer.py,sha256=Na30osfkgrq4VQhUzDcuS52Gy7FS08CzbgEmKwljTmk,671
@@ -143,8 +143,8 @@ ophyd_async/testing/_one_of_everything.py,sha256=Di0hPoKwrDOSsx50-2UdSHM2EbIKrPG
143
143
  ophyd_async/testing/_single_derived.py,sha256=5-HOTzgePcZ354NK_ssVpyIbJoJmKyjVQCxSwQXUC-4,2730
144
144
  ophyd_async/testing/_utils.py,sha256=zClRo5ve8RGia7wQnby41W-Zprj-slOA5da1LfYnuhw,45
145
145
  ophyd_async/testing/_wait_for_pending.py,sha256=YZAR48n-CW0GsPey3zFRzMJ4byDAr3HvMIoawjmTrHw,732
146
- ophyd_async-0.10.0a4.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
147
- ophyd_async-0.10.0a4.dist-info/METADATA,sha256=xs88nrJXsGw254wkPyFNyvQiajcqFFARvmS59_szwmo,7075
148
- ophyd_async-0.10.0a4.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
149
- ophyd_async-0.10.0a4.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
150
- ophyd_async-0.10.0a4.dist-info/RECORD,,
146
+ ophyd_async-0.10.1.dist-info/licenses/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
147
+ ophyd_async-0.10.1.dist-info/METADATA,sha256=T43wg7KEoYRyA7xllFJLv9fM7OvNqYjuA047uX2zNlw,7071
148
+ ophyd_async-0.10.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
149
+ ophyd_async-0.10.1.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
150
+ ophyd_async-0.10.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5