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.
Files changed (157) hide show
  1. ophyd_async/__init__.py +5 -8
  2. ophyd_async/_docs_parser.py +12 -0
  3. ophyd_async/_version.py +9 -4
  4. ophyd_async/core/__init__.py +102 -74
  5. ophyd_async/core/_derived_signal.py +271 -0
  6. ophyd_async/core/_derived_signal_backend.py +300 -0
  7. ophyd_async/core/_detector.py +158 -153
  8. ophyd_async/core/_device.py +143 -115
  9. ophyd_async/core/_device_filler.py +82 -9
  10. ophyd_async/core/_flyer.py +16 -7
  11. ophyd_async/core/_hdf_dataset.py +29 -22
  12. ophyd_async/core/_log.py +14 -23
  13. ophyd_async/core/_mock_signal_backend.py +11 -3
  14. ophyd_async/core/_protocol.py +65 -45
  15. ophyd_async/core/_providers.py +28 -9
  16. ophyd_async/core/_readable.py +74 -58
  17. ophyd_async/core/_settings.py +113 -0
  18. ophyd_async/core/_signal.py +304 -174
  19. ophyd_async/core/_signal_backend.py +60 -14
  20. ophyd_async/core/_soft_signal_backend.py +18 -12
  21. ophyd_async/core/_status.py +72 -24
  22. ophyd_async/core/_table.py +54 -17
  23. ophyd_async/core/_utils.py +101 -52
  24. ophyd_async/core/_yaml_settings.py +66 -0
  25. ophyd_async/epics/__init__.py +1 -0
  26. ophyd_async/epics/adandor/__init__.py +9 -0
  27. ophyd_async/epics/adandor/_andor.py +45 -0
  28. ophyd_async/epics/adandor/_andor_controller.py +51 -0
  29. ophyd_async/epics/adandor/_andor_io.py +34 -0
  30. ophyd_async/epics/adaravis/__init__.py +8 -1
  31. ophyd_async/epics/adaravis/_aravis.py +23 -41
  32. ophyd_async/epics/adaravis/_aravis_controller.py +23 -55
  33. ophyd_async/epics/adaravis/_aravis_io.py +13 -28
  34. ophyd_async/epics/adcore/__init__.py +36 -14
  35. ophyd_async/epics/adcore/_core_detector.py +81 -0
  36. ophyd_async/epics/adcore/_core_io.py +145 -95
  37. ophyd_async/epics/adcore/_core_logic.py +179 -88
  38. ophyd_async/epics/adcore/_core_writer.py +223 -0
  39. ophyd_async/epics/adcore/_hdf_writer.py +51 -92
  40. ophyd_async/epics/adcore/_jpeg_writer.py +26 -0
  41. ophyd_async/epics/adcore/_single_trigger.py +6 -5
  42. ophyd_async/epics/adcore/_tiff_writer.py +26 -0
  43. ophyd_async/epics/adcore/_utils.py +3 -2
  44. ophyd_async/epics/adkinetix/__init__.py +2 -1
  45. ophyd_async/epics/adkinetix/_kinetix.py +32 -27
  46. ophyd_async/epics/adkinetix/_kinetix_controller.py +11 -21
  47. ophyd_async/epics/adkinetix/_kinetix_io.py +12 -13
  48. ophyd_async/epics/adpilatus/__init__.py +7 -2
  49. ophyd_async/epics/adpilatus/_pilatus.py +28 -40
  50. ophyd_async/epics/adpilatus/_pilatus_controller.py +25 -22
  51. ophyd_async/epics/adpilatus/_pilatus_io.py +11 -9
  52. ophyd_async/epics/adsimdetector/__init__.py +8 -1
  53. ophyd_async/epics/adsimdetector/_sim.py +22 -16
  54. ophyd_async/epics/adsimdetector/_sim_controller.py +9 -43
  55. ophyd_async/epics/adsimdetector/_sim_io.py +10 -0
  56. ophyd_async/epics/advimba/__init__.py +10 -1
  57. ophyd_async/epics/advimba/_vimba.py +26 -25
  58. ophyd_async/epics/advimba/_vimba_controller.py +12 -24
  59. ophyd_async/epics/advimba/_vimba_io.py +23 -28
  60. ophyd_async/epics/core/_aioca.py +66 -30
  61. ophyd_async/epics/core/_epics_connector.py +4 -0
  62. ophyd_async/epics/core/_epics_device.py +2 -0
  63. ophyd_async/epics/core/_p4p.py +50 -18
  64. ophyd_async/epics/core/_pvi_connector.py +65 -8
  65. ophyd_async/epics/core/_signal.py +51 -51
  66. ophyd_async/epics/core/_util.py +5 -5
  67. ophyd_async/epics/demo/__init__.py +11 -49
  68. ophyd_async/epics/demo/__main__.py +31 -0
  69. ophyd_async/epics/demo/_ioc.py +32 -0
  70. ophyd_async/epics/demo/_motor.py +82 -0
  71. ophyd_async/epics/demo/_point_detector.py +42 -0
  72. ophyd_async/epics/demo/_point_detector_channel.py +22 -0
  73. ophyd_async/epics/demo/_stage.py +15 -0
  74. ophyd_async/epics/demo/{mover.db → motor.db} +2 -1
  75. ophyd_async/epics/demo/point_detector.db +59 -0
  76. ophyd_async/epics/demo/point_detector_channel.db +21 -0
  77. ophyd_async/epics/eiger/_eiger.py +1 -3
  78. ophyd_async/epics/eiger/_eiger_controller.py +11 -4
  79. ophyd_async/epics/eiger/_eiger_io.py +2 -0
  80. ophyd_async/epics/eiger/_odin_io.py +1 -2
  81. ophyd_async/epics/motor.py +83 -38
  82. ophyd_async/epics/signal.py +4 -1
  83. ophyd_async/epics/testing/__init__.py +14 -14
  84. ophyd_async/epics/testing/_example_ioc.py +68 -73
  85. ophyd_async/epics/testing/_utils.py +19 -44
  86. ophyd_async/epics/testing/test_records.db +16 -0
  87. ophyd_async/epics/testing/test_records_pva.db +17 -16
  88. ophyd_async/fastcs/__init__.py +1 -0
  89. ophyd_async/fastcs/core.py +6 -0
  90. ophyd_async/fastcs/odin/__init__.py +1 -0
  91. ophyd_async/fastcs/panda/__init__.py +8 -8
  92. ophyd_async/fastcs/panda/_block.py +29 -9
  93. ophyd_async/fastcs/panda/_control.py +12 -2
  94. ophyd_async/fastcs/panda/_hdf_panda.py +5 -1
  95. ophyd_async/fastcs/panda/_table.py +13 -7
  96. ophyd_async/fastcs/panda/_trigger.py +23 -9
  97. ophyd_async/fastcs/panda/_writer.py +27 -30
  98. ophyd_async/plan_stubs/__init__.py +16 -0
  99. ophyd_async/plan_stubs/_ensure_connected.py +12 -17
  100. ophyd_async/plan_stubs/_fly.py +3 -5
  101. ophyd_async/plan_stubs/_nd_attributes.py +9 -5
  102. ophyd_async/plan_stubs/_panda.py +14 -0
  103. ophyd_async/plan_stubs/_settings.py +152 -0
  104. ophyd_async/plan_stubs/_utils.py +3 -0
  105. ophyd_async/plan_stubs/_wait_for_awaitable.py +13 -0
  106. ophyd_async/sim/__init__.py +29 -0
  107. ophyd_async/sim/__main__.py +43 -0
  108. ophyd_async/sim/_blob_detector.py +33 -0
  109. ophyd_async/sim/_blob_detector_controller.py +48 -0
  110. ophyd_async/sim/_blob_detector_writer.py +105 -0
  111. ophyd_async/sim/_mirror_horizontal.py +46 -0
  112. ophyd_async/sim/_mirror_vertical.py +74 -0
  113. ophyd_async/sim/_motor.py +233 -0
  114. ophyd_async/sim/_pattern_generator.py +124 -0
  115. ophyd_async/sim/_point_detector.py +86 -0
  116. ophyd_async/sim/_stage.py +19 -0
  117. ophyd_async/tango/__init__.py +1 -0
  118. ophyd_async/tango/core/__init__.py +6 -1
  119. ophyd_async/tango/core/_base_device.py +41 -33
  120. ophyd_async/tango/core/_converters.py +81 -0
  121. ophyd_async/tango/core/_signal.py +21 -33
  122. ophyd_async/tango/core/_tango_readable.py +2 -19
  123. ophyd_async/tango/core/_tango_transport.py +148 -74
  124. ophyd_async/tango/core/_utils.py +47 -0
  125. ophyd_async/tango/demo/_counter.py +2 -0
  126. ophyd_async/tango/demo/_detector.py +2 -0
  127. ophyd_async/tango/demo/_mover.py +10 -6
  128. ophyd_async/tango/demo/_tango/_servers.py +4 -0
  129. ophyd_async/tango/testing/__init__.py +6 -0
  130. ophyd_async/tango/testing/_one_of_everything.py +200 -0
  131. ophyd_async/testing/__init__.py +48 -7
  132. ophyd_async/testing/__pytest_assert_rewrite.py +4 -0
  133. ophyd_async/testing/_assert.py +200 -96
  134. ophyd_async/testing/_mock_signal_utils.py +59 -73
  135. ophyd_async/testing/_one_of_everything.py +146 -0
  136. ophyd_async/testing/_single_derived.py +87 -0
  137. ophyd_async/testing/_utils.py +3 -0
  138. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/METADATA +25 -26
  139. ophyd_async-0.10.0a1.dist-info/RECORD +149 -0
  140. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/WHEEL +1 -1
  141. ophyd_async/core/_device_save_loader.py +0 -274
  142. ophyd_async/epics/demo/_mover.py +0 -95
  143. ophyd_async/epics/demo/_sensor.py +0 -37
  144. ophyd_async/epics/demo/sensor.db +0 -19
  145. ophyd_async/fastcs/panda/_utils.py +0 -16
  146. ophyd_async/sim/demo/__init__.py +0 -19
  147. ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -13
  148. ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -42
  149. ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +0 -62
  150. ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -41
  151. ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -207
  152. ophyd_async/sim/demo/_sim_motor.py +0 -107
  153. ophyd_async/sim/testing/__init__.py +0 -0
  154. ophyd_async-0.9.0a1.dist-info/RECORD +0 -119
  155. ophyd_async-0.9.0a1.dist-info/entry_points.txt +0 -2
  156. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info/licenses}/LICENSE +0 -0
  157. {ophyd_async-0.9.0a1.dist-info → ophyd_async-0.10.0a1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,4 @@
1
- from collections.abc import Iterator, Sequence
2
- from dataclasses import dataclass, field
1
+ from collections.abc import Iterator
3
2
  from pathlib import Path
4
3
  from urllib.parse import urlunparse
5
4
 
@@ -10,44 +9,53 @@ from event_model import (
10
9
  StreamRange,
11
10
  StreamResource,
12
11
  )
12
+ from pydantic import BaseModel, Field
13
13
 
14
14
 
15
- @dataclass
16
- class HDFDataset:
15
+ class HDFDatasetDescription(BaseModel):
16
+ """A description of the type and shape of a dataset in an HDF file."""
17
+
17
18
  data_key: str
19
+ """The data_key that will appear in the event descriptor,
20
+ e.g. det or det.data"""
21
+
18
22
  dataset: str
19
- shape: Sequence[int] = field(default_factory=tuple)
23
+ """The dataset name within the HDF file,
24
+ e.g. /entry/data/data or /entry/instrument/NDAttributes/sum"""
25
+
26
+ shape: tuple[int, ...] = Field(default_factory=tuple)
27
+ """The shape of a single event's data in the HDF file,
28
+ e.g. (1, 768, 1024) for arrays or () for scalars"""
29
+
20
30
  dtype_numpy: str = ""
31
+ """The numpy dtype for this field,
32
+ e.g. <i2 or <f8"""
33
+
34
+ chunk_shape: tuple[int, ...]
35
+ """The explicit chunk size written to disk"""
36
+
21
37
  multiplier: int = 1
22
- swmr: bool = False
23
- # Represents explicit chunk size written to disk.
24
- chunk_shape: tuple[int, ...] = ()
38
+ """Won't be used soon."""
25
39
 
26
40
 
27
41
  SLICE_NAME = "AD_HDF5_SWMR_SLICE"
28
42
 
29
43
 
30
- class HDFFile:
31
- """
32
- :param full_file_name: Absolute path to the file to be written
33
- :param datasets: Datasets to write into the file
44
+ class HDFDocumentComposer:
45
+ """A helper class to make stream resource and datums for HDF datasets.
46
+
47
+ :param full_file_name: Absolute path to the file that has been written
48
+ :param datasets: Descriptions of each of the datasets that will appear in the file
34
49
  """
35
50
 
36
51
  def __init__(
37
52
  self,
38
53
  full_file_name: Path,
39
- datasets: list[HDFDataset],
54
+ datasets: list[HDFDatasetDescription],
40
55
  hostname: str = "localhost",
41
56
  ) -> None:
42
57
  self._last_emitted = 0
43
58
  self._hostname = hostname
44
-
45
- if len(datasets) == 0:
46
- self._bundles = []
47
- return None
48
-
49
- bundler_composer = ComposeStreamResource()
50
-
51
59
  uri = urlunparse(
52
60
  (
53
61
  "file",
@@ -58,7 +66,7 @@ class HDFFile:
58
66
  None,
59
67
  )
60
68
  )
61
-
69
+ bundler_composer = ComposeStreamResource()
62
70
  self._bundles: list[ComposeStreamResourceBundle] = [
63
71
  bundler_composer(
64
72
  mimetype="application/x-hdf5",
@@ -66,7 +74,6 @@ class HDFFile:
66
74
  data_key=ds.data_key,
67
75
  parameters={
68
76
  "dataset": ds.dataset,
69
- "swmr": ds.swmr,
70
77
  "multiplier": ds.multiplier,
71
78
  "chunk_shape": ds.chunk_shape,
72
79
  },
ophyd_async/core/_log.py CHANGED
@@ -63,31 +63,23 @@ def config_ophyd_async_logging(
63
63
  color=True,
64
64
  level="WARNING",
65
65
  ):
66
- """
67
- Set a new handler on the ``logging.getLogger('ophyd_async')`` logger.
66
+ """Set a new handler on the ``logging.getLogger('ophyd_async')`` logger.
67
+
68
68
  If this is called more than once, the handler from the previous invocation
69
69
  is removed (if still present) and replaced.
70
70
 
71
- Parameters
72
- ----------
73
- file : object with ``write`` method or filename string
74
- Default is ``sys.stdout``.
75
- fmt : Overall logging format
76
- datefmt : string
77
- Date format. Default is ``'%H:%M:%S'``.
78
- color : boolean
79
- Use ANSI color codes. True by default.
80
- level : str or int
81
- Python logging level, given as string or corresponding integer.
82
- Default is 'WARNING'.
83
-
84
- Returns
85
- -------
86
- handler : logging.Handler
87
- The handler, which has already been added to the 'ophyd_async' logger.
88
-
89
- Examples
90
- --------
71
+
72
+ :param file:
73
+ object with ``write`` method or filename string. Default is `sys.stdout`.
74
+ :param fmt: str Overall logging format
75
+ :param datefmt: str Date format. Default is `'%H:%M:%S'`.
76
+ :param color: bool Use ANSI color codes. True by default.
77
+ :param level: str or int Python logging level, given as string or
78
+ corresponding integer. Default is 'WARNING'.
79
+
80
+ :returns: The handler, which has already been added to the 'ophyd_async' logger.
81
+
82
+ :examples:
91
83
  Log to a file.
92
84
 
93
85
  config_ophyd_async_logging(file='/tmp/what_is_happening.txt')
@@ -104,7 +96,6 @@ def config_ophyd_async_logging(
104
96
  Increase verbosity: show level DEBUG or higher.
105
97
 
106
98
  config_ophyd_async_logging(level='DEBUG')
107
-
108
99
  """
109
100
  global current_handler
110
101
 
@@ -3,8 +3,10 @@ from collections.abc import Callable
3
3
  from functools import cached_property
4
4
  from unittest.mock import AsyncMock
5
5
 
6
- from bluesky.protocols import Descriptor, Reading
6
+ from bluesky.protocols import Reading
7
+ from event_model import DataKey
7
8
 
9
+ from ._derived_signal_backend import DerivedSignalBackend
8
10
  from ._signal_backend import SignalBackend, SignalDatatypeT
9
11
  from ._soft_signal_backend import SoftSignalBackend
10
12
  from ._utils import Callback, LazyMock
@@ -23,7 +25,7 @@ class MockSignalBackend(SignalBackend[SignalDatatypeT]):
23
25
 
24
26
  self.initial_backend = initial_backend
25
27
 
26
- if isinstance(self.initial_backend, SoftSignalBackend):
28
+ if isinstance(self.initial_backend, SoftSignalBackend | DerivedSignalBackend):
27
29
  # Backend is already a SoftSignalBackend, so use it
28
30
  self.soft_backend = self.initial_backend
29
31
  else:
@@ -38,11 +40,13 @@ class MockSignalBackend(SignalBackend[SignalDatatypeT]):
38
40
 
39
41
  @cached_property
40
42
  def put_mock(self) -> AsyncMock:
43
+ """Return the mock that will track calls to `put()`."""
41
44
  put_mock = AsyncMock(name="put", spec=Callable)
42
45
  self.mock().attach_mock(put_mock, "put")
43
46
  return put_mock
44
47
 
45
48
  def set_value(self, value: SignalDatatypeT):
49
+ """Set the value of the signal."""
46
50
  self.soft_backend.set_value(value)
47
51
 
48
52
  def source(self, name: str, read: bool) -> str:
@@ -53,6 +57,10 @@ class MockSignalBackend(SignalBackend[SignalDatatypeT]):
53
57
 
54
58
  @cached_property
55
59
  def put_proceeds(self) -> asyncio.Event:
60
+ """Return an Event that will block `put()` until set.
61
+
62
+ The Event is initially set, but can be unset to block `put()`.
63
+ """
56
64
  put_proceeds = asyncio.Event()
57
65
  put_proceeds.set()
58
66
  return put_proceeds
@@ -72,7 +80,7 @@ class MockSignalBackend(SignalBackend[SignalDatatypeT]):
72
80
  async def get_setpoint(self) -> SignalDatatypeT:
73
81
  return await self.soft_backend.get_setpoint()
74
82
 
75
- async def get_datakey(self, source: str) -> Descriptor:
83
+ async def get_datakey(self, source: str) -> DataKey:
76
84
  return await self.soft_backend.get_datakey(source)
77
85
 
78
86
  def set_callback(self, callback: Callback[Reading[SignalDatatypeT]] | None) -> None:
@@ -10,67 +10,66 @@ from typing import (
10
10
  runtime_checkable,
11
11
  )
12
12
 
13
- from bluesky.protocols import HasName, Reading
13
+ from bluesky.protocols import HasName, Location, Reading, T_co
14
14
  from event_model import DataKey
15
15
 
16
+ from ._utils import T
17
+
16
18
  if TYPE_CHECKING:
17
19
  from ._status import AsyncStatus
18
20
 
19
21
 
20
22
  @runtime_checkable
21
23
  class AsyncReadable(HasName, Protocol):
24
+ """Async implementations of the sync [](#bluesky.protocols.Readable)."""
25
+
22
26
  @abstractmethod
23
27
  async def read(self) -> dict[str, Reading]:
24
- """Return an OrderedDict mapping string field name(s) to dictionaries
25
- of values and timestamps and optional per-point metadata.
26
-
27
- Example return value:
28
+ """Return value, timestamp, optional per-point metadata for each field name.
28
29
 
29
- .. code-block:: python
30
+ For example:
30
31
 
31
- OrderedDict(('channel1',
32
- {'value': 5, 'timestamp': 1472493713.271991}),
33
- ('channel2',
34
- {'value': 16, 'timestamp': 1472493713.539238}))
32
+ {
33
+ "channel1": {"value": 5, "timestamp": 1472493713.271991},
34
+ "channel2": {"value": 16, "timestamp": 1472493713.539238},
35
+ }
35
36
  """
36
37
 
37
38
  @abstractmethod
38
39
  async def describe(self) -> dict[str, DataKey]:
39
- """Return an OrderedDict with exactly the same keys as the ``read``
40
- method, here mapped to per-scan metadata about each field.
40
+ """Return per-scan metadata for each field name in `read()`.
41
41
 
42
- Example return value:
42
+ For example:
43
43
 
44
- .. code-block:: python
45
-
46
- OrderedDict(('channel1',
47
- {'source': 'XF23-ID:SOME_PV_NAME',
48
- 'dtype': 'number',
49
- 'shape': []}),
50
- ('channel2',
51
- {'source': 'XF23-ID:SOME_PV_NAME',
52
- 'dtype': 'number',
53
- 'shape': []}))
44
+ {
45
+ "channel1": {"source": "SOME_PV1", "dtype": "number", "shape": []},
46
+ "channel2": {"source": "SOME_PV2", "dtype": "number", "shape": []},
47
+ }
54
48
  """
55
49
 
56
50
 
57
51
  @runtime_checkable
58
52
  class AsyncConfigurable(HasName, Protocol):
53
+ """Async implementation of the sync [](#bluesky.protocols.Configurable)."""
54
+
59
55
  @abstractmethod
60
56
  async def read_configuration(self) -> dict[str, Reading]:
61
- """Same API as ``read`` but for slow-changing fields related to configuration.
62
- e.g., exposure time. These will typically be read only once per run.
57
+ """Return value, timestamp, optional per-point metadata for each field name.
58
+
59
+ Same API as [](#AsyncReadable.read) but for slow-changing fields related to
60
+ configuration. e.g., exposure time. These will typically be read only
61
+ once per run.
63
62
  """
64
63
 
65
64
  @abstractmethod
66
65
  async def describe_configuration(self) -> dict[str, DataKey]:
67
- """Same API as ``describe``, but corresponding to the keys in
68
- ``read_configuration``.
69
- """
66
+ """Return per-scan metadata for each field name in `read_configuration()`."""
70
67
 
71
68
 
72
69
  @runtime_checkable
73
70
  class AsyncPausable(Protocol):
71
+ """Async implementation of the sync [](#bluesky.protocols.Pausable)."""
72
+
74
73
  @abstractmethod
75
74
  async def pause(self) -> None:
76
75
  """Perform device-specific work when the RunEngine pauses."""
@@ -82,20 +81,40 @@ class AsyncPausable(Protocol):
82
81
 
83
82
  @runtime_checkable
84
83
  class AsyncStageable(Protocol):
84
+ """Async implementation of the sync [](#bluesky.protocols.Stageable)."""
85
+
85
86
  @abstractmethod
86
87
  def stage(self) -> AsyncStatus:
87
- """An optional hook for "setting up" the device for acquisition.
88
+ """Set up the device for acquisition.
88
89
 
89
- It should return a ``Status`` that is marked done when the device is
90
- done staging.
90
+ :return: An `AsyncStatus` that is marked done when the device is done staging.
91
91
  """
92
92
 
93
93
  @abstractmethod
94
94
  def unstage(self) -> AsyncStatus:
95
- """A hook for "cleaning up" the device after acquisition.
95
+ """Clean up the device after acquisition.
96
+
97
+ :return: An `AsyncStatus` that is marked done when the device is done unstaging.
98
+ """
99
+
100
+
101
+ @runtime_checkable
102
+ class AsyncMovable(Protocol[T_co]):
103
+ @abstractmethod
104
+ def set(self, value: T_co) -> AsyncStatus:
105
+ """Return a ``Status`` that is marked done when the device is done moving."""
96
106
 
97
- It should return a ``Status`` that is marked done when the device is finished
98
- unstaging.
107
+
108
+ @runtime_checkable
109
+ class AsyncLocatable(AsyncMovable[T], Protocol):
110
+ @abstractmethod
111
+ async def locate(self) -> Location[T]:
112
+ """Return the current location of a Device.
113
+
114
+ While a ``Readable`` reports many values, a ``Movable`` will have the
115
+ concept of location. This is where the Device currently is, and where it
116
+ was last requested to move to. This protocol formalizes how to get the
117
+ location from a ``Movable``.
99
118
  """
100
119
 
101
120
 
@@ -103,16 +122,17 @@ C = TypeVar("C", contravariant=True)
103
122
 
104
123
 
105
124
  class Watcher(Protocol, Generic[C]):
106
- @staticmethod
125
+ """Protocol for watching changes in values."""
126
+
107
127
  def __call__(
108
- *,
109
- current: C,
110
- initial: C,
111
- target: C,
112
- name: str | None,
113
- unit: str | None,
114
- precision: float | None,
115
- fraction: float | None,
116
- time_elapsed: float | None,
117
- time_remaining: float | None,
128
+ self,
129
+ current: C | None = None,
130
+ initial: C | None = None,
131
+ target: C | None = None,
132
+ name: str | None = None,
133
+ unit: str | None = None,
134
+ precision: int | None = None,
135
+ fraction: float | None = None,
136
+ time_elapsed: float | None = None,
137
+ time_remaining: float | None = None,
118
138
  ) -> Any: ...
@@ -10,8 +10,7 @@ from typing import Protocol
10
10
 
11
11
  @dataclass
12
12
  class PathInfo:
13
- """
14
- Information about where and how to write a file.
13
+ """Information about where and how to write a file.
15
14
 
16
15
  :param directory_path: Directory into which files should be written
17
16
  :param filename: Base filename to use generated by FilenameProvider, w/o extension
@@ -25,18 +24,24 @@ class PathInfo:
25
24
 
26
25
 
27
26
  class FilenameProvider(Protocol):
27
+ """Base class for callable classes providing filenames."""
28
+
28
29
  @abstractmethod
29
30
  def __call__(self, device_name: str | None = None) -> str:
30
- """Get a filename to use for output data, w/o extension"""
31
+ """Get a filename to use for output data, w/o extension."""
31
32
 
32
33
 
33
34
  class PathProvider(Protocol):
35
+ """Abstract class that tells a detector where to write its data."""
36
+
34
37
  @abstractmethod
35
38
  def __call__(self, device_name: str | None = None) -> PathInfo:
36
- """Get the current directory to write files into"""
39
+ """Get the current directory to write files into."""
37
40
 
38
41
 
39
42
  class StaticFilenameProvider(FilenameProvider):
43
+ """Provides a constant filename on every call."""
44
+
40
45
  def __init__(self, filename: str):
41
46
  self._static_filename = filename
42
47
 
@@ -45,6 +50,8 @@ class StaticFilenameProvider(FilenameProvider):
45
50
 
46
51
 
47
52
  class UUIDFilenameProvider(FilenameProvider):
53
+ """Files will have a UUID as a filename."""
54
+
48
55
  def __init__(
49
56
  self,
50
57
  uuid_call_func: Callable = uuid.uuid4,
@@ -68,6 +75,8 @@ class UUIDFilenameProvider(FilenameProvider):
68
75
 
69
76
 
70
77
  class AutoIncrementFilenameProvider(FilenameProvider):
78
+ """Provides a new numerically incremented filename on each call."""
79
+
71
80
  def __init__(
72
81
  self,
73
82
  base_filename: str = "",
@@ -98,14 +107,16 @@ class AutoIncrementFilenameProvider(FilenameProvider):
98
107
 
99
108
 
100
109
  class StaticPathProvider(PathProvider):
110
+ """All files will be within a static directory."""
111
+
101
112
  def __init__(
102
113
  self,
103
114
  filename_provider: FilenameProvider,
104
- directory_path: Path,
115
+ directory_path: Path | str,
105
116
  create_dir_depth: int = 0,
106
117
  ) -> None:
107
118
  self._filename_provider = filename_provider
108
- self._directory_path = directory_path
119
+ self._directory_path = Path(directory_path)
109
120
  self._create_dir_depth = create_dir_depth
110
121
 
111
122
  def __call__(self, device_name: str | None = None) -> PathInfo:
@@ -119,6 +130,8 @@ class StaticPathProvider(PathProvider):
119
130
 
120
131
 
121
132
  class AutoIncrementingPathProvider(PathProvider):
133
+ """Provides a new numerically incremented path on each call."""
134
+
122
135
  def __init__(
123
136
  self,
124
137
  filename_provider: FilenameProvider,
@@ -169,6 +182,8 @@ class AutoIncrementingPathProvider(PathProvider):
169
182
 
170
183
 
171
184
  class YMDPathProvider(PathProvider):
185
+ """Provides a path with the date included in the directory name."""
186
+
172
187
  def __init__(
173
188
  self,
174
189
  filename_provider: FilenameProvider,
@@ -206,16 +221,20 @@ class YMDPathProvider(PathProvider):
206
221
 
207
222
 
208
223
  class NameProvider(Protocol):
224
+ """Base class for callable classes providing data keys."""
225
+
209
226
  @abstractmethod
210
227
  def __call__(self) -> str:
211
- """Get the name to be used as a data_key in the descriptor document"""
228
+ """Get the name to be used as a data_key in the descriptor document."""
212
229
 
213
230
 
214
231
  class DatasetDescriber(Protocol):
232
+ """For describing datasets in file writing."""
233
+
215
234
  @abstractmethod
216
235
  async def np_datatype(self) -> str:
217
- """Represents the numpy datatype"""
236
+ """Return the numpy datatype for this dataset."""
218
237
 
219
238
  @abstractmethod
220
239
  async def shape(self) -> tuple[int, ...]:
221
- """Get the shape of the data collection"""
240
+ """Get the shape of the data collection."""