ophyd-async 0.8.0a4__py3-none-any.whl → 0.8.0a5__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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.8.0a4'
15
+ __version__ = version = '0.8.0a5'
16
16
  __version_tuple__ = version_tuple = (0, 8, 0)
@@ -37,8 +37,14 @@ class DeviceConnector:
37
37
 
38
38
  async def connect_mock(self, device: Device, mock: LazyMock):
39
39
  # Connect serially, no errors to gather up as in mock mode
40
+ exceptions: dict[str, Exception] = {}
40
41
  for name, child_device in device.children():
41
- await child_device.connect(mock=mock.child(name))
42
+ try:
43
+ await child_device.connect(mock=mock.child(name))
44
+ except Exception as e:
45
+ exceptions[name] = e
46
+ if exceptions:
47
+ raise NotConnected.with_other_exceptions_logged(exceptions)
42
48
 
43
49
  async def connect_real(self, device: Device, timeout: float, force_reconnect: bool):
44
50
  """Used during ``Device.connect``.
@@ -193,7 +199,8 @@ class DeviceVector(MutableMapping[int, DeviceT], Device):
193
199
  children: Mapping[int, DeviceT],
194
200
  name: str = "",
195
201
  ) -> None:
196
- self._children = dict(children)
202
+ self._children: dict[int, DeviceT] = {}
203
+ self.update(children)
197
204
  super().__init__(name=name)
198
205
 
199
206
  def __setattr__(self, name: str, child: Any) -> None:
@@ -449,12 +449,6 @@ async def observe_value(
449
449
  """
450
450
 
451
451
  q: asyncio.Queue[SignalDatatypeT | Status] = asyncio.Queue()
452
- if timeout is None:
453
- get_value = q.get
454
- else:
455
-
456
- async def get_value():
457
- return await asyncio.wait_for(q.get(), timeout)
458
452
 
459
453
  if done_status is not None:
460
454
  done_status.add_callback(q.put_nowait)
@@ -462,7 +456,10 @@ async def observe_value(
462
456
  signal.subscribe_value(q.put_nowait)
463
457
  try:
464
458
  while True:
465
- item = await get_value()
459
+ # yield here in case something else is filling the queue
460
+ # like in test_observe_value_times_out_with_no_external_task()
461
+ await asyncio.sleep(0)
462
+ item = await asyncio.wait_for(q.get(), timeout)
466
463
  if done_status and item is done_status:
467
464
  if exc := done_status.exception():
468
465
  raise exc
@@ -5,7 +5,7 @@ from abc import abstractmethod
5
5
  from collections.abc import Sequence
6
6
  from dataclasses import dataclass
7
7
  from functools import lru_cache
8
- from typing import Any, Generic, get_origin
8
+ from typing import Any, Generic, get_args, get_origin
9
9
 
10
10
  import numpy as np
11
11
  from bluesky.protocols import Reading
@@ -58,7 +58,7 @@ class SequenceEnumSoftConverter(SoftConverter[Sequence[EnumT]]):
58
58
 
59
59
  @dataclass
60
60
  class NDArraySoftConverter(SoftConverter[Array1D]):
61
- datatype: np.dtype
61
+ datatype: np.dtype | None = None
62
62
 
63
63
  def write_value(self, value: Any) -> Array1D:
64
64
  return np.array(() if value is None else value, dtype=self.datatype)
@@ -98,7 +98,11 @@ def make_converter(datatype: type[SignalDatatype]) -> SoftConverter:
98
98
  return SequenceStrSoftConverter()
99
99
  elif get_origin(datatype) == Sequence and enum_cls:
100
100
  return SequenceEnumSoftConverter(enum_cls)
101
+ elif datatype is np.ndarray:
102
+ return NDArraySoftConverter()
101
103
  elif get_origin(datatype) == np.ndarray:
104
+ if datatype not in get_args(SignalDatatype):
105
+ raise TypeError(f"Expected Array1D[dtype], got {datatype}")
102
106
  return NDArraySoftConverter(get_dtype(datatype))
103
107
  elif enum_cls:
104
108
  return EnumSoftConverter(enum_cls)
@@ -2,18 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
- from collections.abc import Awaitable, Callable, Iterable, Sequence
5
+ from collections.abc import Awaitable, Callable, Iterable, Mapping, Sequence
6
6
  from dataclasses import dataclass
7
7
  from enum import Enum, EnumMeta
8
- from typing import (
9
- Any,
10
- Generic,
11
- Literal,
12
- ParamSpec,
13
- TypeVar,
14
- get_args,
15
- get_origin,
16
- )
8
+ from typing import Any, Generic, Literal, ParamSpec, TypeVar, get_args, get_origin
17
9
  from unittest.mock import Mock
18
10
 
19
11
  import numpy as np
@@ -22,7 +14,7 @@ T = TypeVar("T")
22
14
  P = ParamSpec("P")
23
15
  Callback = Callable[[T], None]
24
16
  DEFAULT_TIMEOUT = 10.0
25
- ErrorText = str | dict[str, Exception]
17
+ ErrorText = str | Mapping[str, Exception]
26
18
 
27
19
 
28
20
  class StrictEnum(str, Enum):
@@ -70,6 +62,13 @@ class NotConnected(Exception):
70
62
 
71
63
  self._errors = errors
72
64
 
65
+ @property
66
+ def sub_errors(self) -> Mapping[str, Exception]:
67
+ if isinstance(self._errors, dict):
68
+ return self._errors.copy()
69
+ else:
70
+ return {}
71
+
73
72
  def _format_sub_errors(self, name: str, error: Exception, indent="") -> str:
74
73
  if isinstance(error, NotConnected):
75
74
  error_txt = ":" + error.format_error_string(indent + self._indent_width)
@@ -100,6 +99,19 @@ class NotConnected(Exception):
100
99
  def __str__(self) -> str:
101
100
  return self.format_error_string(indent="")
102
101
 
102
+ @classmethod
103
+ def with_other_exceptions_logged(
104
+ cls, exceptions: Mapping[str, Exception]
105
+ ) -> NotConnected:
106
+ for name, exception in exceptions.items():
107
+ if not isinstance(exception, NotConnected):
108
+ logging.exception(
109
+ f"device `{name}` raised unexpected exception "
110
+ f"{type(exception).__name__}",
111
+ exc_info=exception,
112
+ )
113
+ return NotConnected(exceptions)
114
+
103
115
 
104
116
  @dataclass(frozen=True)
105
117
  class WatcherUpdate(Generic[T]):
@@ -137,14 +149,7 @@ async def wait_for_connection(**coros: Awaitable[None]):
137
149
  exceptions[name] = result
138
150
 
139
151
  if exceptions:
140
- for name, exception in exceptions.items():
141
- if not isinstance(exception, NotConnected):
142
- logging.exception(
143
- f"device `{name}` raised unexpected exception "
144
- f"{type(exception).__name__}",
145
- exc_info=exception,
146
- )
147
- raise NotConnected(exceptions)
152
+ raise NotConnected.with_other_exceptions_logged(exceptions)
148
153
 
149
154
 
150
155
  def get_dtype(datatype: type) -> np.dtype:
@@ -115,7 +115,7 @@ class CaLongStrConverter(CaConverter[str]):
115
115
  def __init__(self):
116
116
  super().__init__(str, dbr.DBR_CHAR_STR, dbr.DBR_CHAR_STR)
117
117
 
118
- def write_value_and_dbr(self, value: Any) -> Any:
118
+ def write_value(self, value: Any) -> Any:
119
119
  # Add a null in here as this is what the commandline caput does
120
120
  # TODO: this should be in the server so check if it can be pushed to asyn
121
121
  return value + "\0"
@@ -188,6 +188,7 @@ _datatype_converter_from_typeid: dict[
188
188
  ("epics:nt/NTScalarArray:1.0", "as"): (Sequence[str], PvaConverter),
189
189
  ("epics:nt/NTTable:1.0", "S"): (Table, PvaTableConverter),
190
190
  ("epics:nt/NTNDArray:1.0", "v"): (np.ndarray, PvaNDArrayConverter),
191
+ ("epics:nt/NTNDArray:1.0", "U"): (np.ndarray, PvaNDArrayConverter),
191
192
  }
192
193
 
193
194
 
@@ -13,7 +13,7 @@ class PandaHdf5DatasetType(StrictEnum):
13
13
 
14
14
  class DatasetTable(Table):
15
15
  name: Sequence[str]
16
- hdf5_type: Sequence[PandaHdf5DatasetType]
16
+ dtype: Sequence[PandaHdf5DatasetType]
17
17
 
18
18
 
19
19
  class SeqTrigger(StrictEnum):
@@ -1,3 +1,5 @@
1
+ from collections.abc import Awaitable
2
+
1
3
  import bluesky.plan_stubs as bps
2
4
 
3
5
  from ophyd_async.core import DEFAULT_TIMEOUT, Device, LazyMock, wait_for_connection
@@ -9,18 +11,23 @@ def ensure_connected(
9
11
  timeout: float = DEFAULT_TIMEOUT,
10
12
  force_reconnect=False,
11
13
  ):
12
- (connect_task,) = yield from bps.wait_for(
13
- [
14
- lambda: wait_for_connection(
15
- **{
16
- device.name: device.connect(
17
- mock=mock, timeout=timeout, force_reconnect=force_reconnect
18
- )
19
- for device in devices
20
- }
14
+ device_names = [device.name for device in devices]
15
+ non_unique = {
16
+ device: device.name for device in devices if device_names.count(device.name) > 1
17
+ }
18
+ if non_unique:
19
+ raise ValueError(f"Devices do not have unique names {non_unique}")
20
+
21
+ def connect_devices() -> Awaitable[None]:
22
+ coros = {
23
+ device.name: device.connect(
24
+ mock=mock, timeout=timeout, force_reconnect=force_reconnect
21
25
  )
22
- ]
23
- )
26
+ for device in devices
27
+ }
28
+ return wait_for_connection(**coros)
29
+
30
+ (connect_task,) = yield from bps.wait_for([connect_devices])
24
31
 
25
32
  if connect_task and connect_task.exception() is not None:
26
33
  raise connect_task.exception()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ophyd-async
3
- Version: 0.8.0a4
3
+ Version: 0.8.0a5
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,10 +1,10 @@
1
1
  ophyd_async/__init__.py,sha256=tEfgj45lRItQ-_u8SRFPM-mpBh3gWvHXr3emhiJJG_M,225
2
2
  ophyd_async/__main__.py,sha256=n_U4O9bgm97OuboUB_9eK7eFiwy8BZSgXJ0OzbE0DqU,481
3
- ophyd_async/_version.py,sha256=_oysVx3AtIs7YHDdNWu47vFG8nzgweLVt7KUik2-u0Q,413
3
+ ophyd_async/_version.py,sha256=SQdFsQ53whTNOERBL9cmDWrar7r4kVAXQthVKIJB7mU,413
4
4
  ophyd_async/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  ophyd_async/core/__init__.py,sha256=JBKq2w50jBjxg_2KRL1jXnIzETaj8dPOD47_prgtotU,4283
6
6
  ophyd_async/core/_detector.py,sha256=bKLekM2J3GzLXsKwe8qXQjNP_sAVsa8EtwFEWD-8MeA,14307
7
- ophyd_async/core/_device.py,sha256=AMuYtsWtce5rebeHvb5bntZkNLpfnJV9XgQLp0zd5Ks,11802
7
+ ophyd_async/core/_device.py,sha256=ygXxDKiTO43qDnLeLzrKKyABwlrfGvSVii7PHyCIjHg,12074
8
8
  ophyd_async/core/_device_filler.py,sha256=Nw-DUyuXYpvt4mmCAQaNVA0LFBBaPK84ubZo3bR39Ak,11407
9
9
  ophyd_async/core/_device_save_loader.py,sha256=OViN9_LWNOLuajzrHDKYEqd5I47u5npQACdGceKcIGY,8375
10
10
  ophyd_async/core/_flyer.py,sha256=us5z6MNGCvIfgPDTmFTxNERSP37g0WVRkRD0Z2JiMgM,1701
@@ -15,12 +15,12 @@ ophyd_async/core/_mock_signal_utils.py,sha256=YeKjStClwp1etlmHMx1tb_VV1GjeFPg83H
15
15
  ophyd_async/core/_protocol.py,sha256=MuYRqSfakdry9RllX7G9UTzp4lw3eDjtkdGPpnbNb34,4040
16
16
  ophyd_async/core/_providers.py,sha256=ff9ZT5-PZ6rhTTdE-q8w9l_k9DuZqLWLebsKZLeJ0Ds,7112
17
17
  ophyd_async/core/_readable.py,sha256=7FxqxhAT1wBQqOEivgnY731zA9QoK1Tt-ZGcH7GBOXM,10623
18
- ophyd_async/core/_signal.py,sha256=HO3XkSvs_5t6yJcZAHCaxOfGy8AE0B9c9sDwlG4x21g,19847
18
+ ophyd_async/core/_signal.py,sha256=IpOyYu1IqpX8sFs9_CLUd1LuFWgAqEaFZgA12BG5tfM,19897
19
19
  ophyd_async/core/_signal_backend.py,sha256=YWPgLSPbfPnWIUDHvP1ArCVK8zKXJxzzbloqQe_ucCI,5040
20
- ophyd_async/core/_soft_signal_backend.py,sha256=d74wML22E3H81W6xsPIj44ghw3jP51Jph4vCLGFwB2k,5706
20
+ ophyd_async/core/_soft_signal_backend.py,sha256=w9zzD4eoD9SsJpORXNSaFOLJrD6biYBbCSVAybLa_7k,5926
21
21
  ophyd_async/core/_status.py,sha256=OUKhblRQ4KU5PDsWbpvYduM7G60JMk1NqeV4eqyPtKc,5131
22
22
  ophyd_async/core/_table.py,sha256=ZToBVmAPDmhrVDgjx0f8SErxVdKhvGdGwQ-fXxGCtN8,5386
23
- ophyd_async/core/_utils.py,sha256=230vayCyT1xsZDjpr7JRcax8zYTxImsf9gm5GiZtnZ8,9132
23
+ ophyd_async/core/_utils.py,sha256=wzzGL7yPAMuPueGOG1cpTgh0vho5YxI86m8SSX7Z9hw,9494
24
24
  ophyd_async/epics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  ophyd_async/epics/motor.py,sha256=pujJXV_vslvo3AxsVySTAEoFuduuv5Hp6sz8aRvIbeQ,8792
26
26
  ophyd_async/epics/signal.py,sha256=hJCGIIWjRVhjEHkeL1I_oPEaaN7dDFKmm7G7ZmgoTYQ,219
@@ -50,10 +50,10 @@ ophyd_async/epics/advimba/_vimba.py,sha256=E_RJ0uJQt-RWAY7uFTNkHaOdGYS5sa7ZbRgAe
50
50
  ophyd_async/epics/advimba/_vimba_controller.py,sha256=Ej7irxTab9cfjmqz4G4Zxv3CjhJw_eRmIb3E62YWh6g,2226
51
51
  ophyd_async/epics/advimba/_vimba_io.py,sha256=F3KUzMN-GMe637za-iRVGGTt8v9F1a79fRcl3MCkfXA,1864
52
52
  ophyd_async/epics/core/__init__.py,sha256=8NoQxEEc2Ny_L9nrD2fnGSf_2gJr1wCR1LwUeLNcIJo,588
53
- ophyd_async/epics/core/_aioca.py,sha256=318lw_dGWnckgyQ1f4K8QeX_KVOD4idzWX8sx2jbnyk,11625
53
+ ophyd_async/epics/core/_aioca.py,sha256=iQWiHWYbMJLa7qeBrCz4_e16Y8A-NYYi6oYNi8oOFVY,11617
54
54
  ophyd_async/epics/core/_epics_connector.py,sha256=n1FlQYui8HdobPxaX3VAflrzi2UT7QCe3cFasssmVLw,1789
55
55
  ophyd_async/epics/core/_epics_device.py,sha256=kshNiKQhevsL2OZXa-r093L_sQGvGK_0J4PWVLg3Eqw,437
56
- ophyd_async/epics/core/_p4p.py,sha256=Ap_WVWCa4Eb44i50bxjy8qS-n8AUKTaLbFNq5eZZk0w,14619
56
+ ophyd_async/epics/core/_p4p.py,sha256=S6zXXApRF0454aOcxUI_cd7Y7tXiOnss_ODhjjk0PMo,14691
57
57
  ophyd_async/epics/core/_pvi_connector.py,sha256=Rjc8g3Rdny_O-4JxhoCpD4L7XWIRq-lnGHXKpsIUrSU,3621
58
58
  ophyd_async/epics/core/_signal.py,sha256=jHdMXV1-0bd7PC8XV32Sso1xgubZVDhWFNsWV-UuamQ,4642
59
59
  ophyd_async/epics/core/_util.py,sha256=6CCWDfp54WeBIJdGjg_YBVZTKoNjponWyykMmLPrj7U,1820
@@ -74,12 +74,12 @@ ophyd_async/fastcs/panda/__init__.py,sha256=_o7n7ckoTM6hTRHpLphpL7r_9sADE59MRNM0
74
74
  ophyd_async/fastcs/panda/_block.py,sha256=STQo6NJAqIVfxyMf-2pxINPyr9_nKtXSdicp92a25xo,1709
75
75
  ophyd_async/fastcs/panda/_control.py,sha256=61vcJMjYQiUGAM5J0SfkfthFs7U28m9Pe9mgmGGf0-w,1021
76
76
  ophyd_async/fastcs/panda/_hdf_panda.py,sha256=WdgWgdrU2yT4keH70VG-ZBVOmT-IpKVyukEuKk7QnJs,1049
77
- ophyd_async/fastcs/panda/_table.py,sha256=8XkfpdZETflzoZE4qB5Y5ySi8JFxxZulDszVccgY_w4,2293
77
+ ophyd_async/fastcs/panda/_table.py,sha256=5YyAfsl3H7kxH3bDjUKHuH9DyrWQmAn9dv-v0NYzFNo,2289
78
78
  ophyd_async/fastcs/panda/_trigger.py,sha256=forImtdnDnaZ0KKhqSxCqwHWXq13SJ4mn9wdM4yqNLY,3056
79
79
  ophyd_async/fastcs/panda/_utils.py,sha256=NdvzdKy0SOG1eCVMQo_nwRXpBo0wyi6lM5Xw3HvssOw,508
80
80
  ophyd_async/fastcs/panda/_writer.py,sha256=wDN6uWX1ENofmI3JBXJ7_CGooI7WsZP-JJQrRiSc6sM,6000
81
81
  ophyd_async/plan_stubs/__init__.py,sha256=wjpEj_BoBZJ9x2fhUPY6BzWMqyYH96JrBlJvV7frdN4,524
82
- ophyd_async/plan_stubs/_ensure_connected.py,sha256=ofMDgOLc7SyR8SVA1hY_zvfkNLo1g5jbRU27W3ICSS0,732
82
+ ophyd_async/plan_stubs/_ensure_connected.py,sha256=uoqfAzghjifdfD_JM860TvMvj9T2Y12nKPvtI5l6zZc,1021
83
83
  ophyd_async/plan_stubs/_fly.py,sha256=WxghBAHsF-8xFrILCm44jeHIu9udLhm-tj4JXd9kZjY,6208
84
84
  ophyd_async/plan_stubs/_nd_attributes.py,sha256=TVfy3bhnrLFBXZ6b2bREBj0LzEviEGzuGvgWK3I7tII,2198
85
85
  ophyd_async/sim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -104,9 +104,9 @@ ophyd_async/tango/demo/_tango/_servers.py,sha256=MwkkoZWJQm_cgafCBBXeQfwyAiOgU8c
104
104
  ophyd_async/tango/signal/__init__.py,sha256=-_wBvhSPb58h_XSeGVaJ6gMFOY8TQNsVYfZxQuxGB1c,750
105
105
  ophyd_async/tango/signal/_signal.py,sha256=72iOxCt6HkyaYPgE402h5fd1KryyVUarR0exV2A3UbU,6277
106
106
  ophyd_async/tango/signal/_tango_transport.py,sha256=DVTdLu8C19k-QzYaKUzFK2WMbaSd6dIO77k99ugD8U4,28990
107
- ophyd_async-0.8.0a4.dist-info/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
108
- ophyd_async-0.8.0a4.dist-info/METADATA,sha256=W1JjO8G4VcMybLwHqOkiySyWLZ_f_TCwTLA54Hf64Sg,6708
109
- ophyd_async-0.8.0a4.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
110
- ophyd_async-0.8.0a4.dist-info/entry_points.txt,sha256=O0YNJTEufO0w9BozXi-JurTy2U1_o0ypeCgJLQ727Jk,58
111
- ophyd_async-0.8.0a4.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
112
- ophyd_async-0.8.0a4.dist-info/RECORD,,
107
+ ophyd_async-0.8.0a5.dist-info/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
108
+ ophyd_async-0.8.0a5.dist-info/METADATA,sha256=0uu4GmEt1a33pHw4Oa5BhF22ns92xsqC_0wNmg3nkM0,6708
109
+ ophyd_async-0.8.0a5.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
110
+ ophyd_async-0.8.0a5.dist-info/entry_points.txt,sha256=O0YNJTEufO0w9BozXi-JurTy2U1_o0ypeCgJLQ727Jk,58
111
+ ophyd_async-0.8.0a5.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
112
+ ophyd_async-0.8.0a5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.4.0)
2
+ Generator: setuptools (75.5.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5