ophyd-async 0.14.1__tar.gz → 0.15__tar.gz

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 (331) hide show
  1. {ophyd_async-0.14.1 → ophyd_async-0.15}/.copier-answers.yml +1 -1
  2. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/CONTRIBUTING.md +1 -1
  3. {ophyd_async-0.14.1/src/ophyd_async.egg-info → ophyd_async-0.15}/PKG-INFO +1 -1
  4. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/conf.py +3 -0
  5. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/interact-with-signals.md +34 -9
  6. {ophyd_async-0.14.1 → ophyd_async-0.15}/pyproject.toml +1 -1
  7. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/_version.py +3 -3
  8. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/__init__.py +17 -5
  9. ophyd_async-0.14.1/src/ophyd_async/core/_table.py → ophyd_async-0.15/src/ophyd_async/core/_datatypes.py +18 -9
  10. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_derived_signal.py +57 -24
  11. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_derived_signal_backend.py +1 -5
  12. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_device_filler.py +30 -7
  13. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_mock_signal_backend.py +25 -7
  14. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_mock_signal_utils.py +7 -11
  15. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_signal.py +11 -11
  16. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_signal_backend.py +7 -19
  17. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_soft_signal_backend.py +6 -6
  18. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_status.py +81 -4
  19. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_utils.py +57 -7
  20. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_core_io.py +12 -5
  21. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_core_logic.py +1 -1
  22. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/__init__.py +2 -1
  23. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_aioca.py +13 -3
  24. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_epics_connector.py +4 -1
  25. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_p4p.py +13 -3
  26. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_signal.py +18 -6
  27. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_util.py +23 -3
  28. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/_motor.py +2 -2
  29. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/motor.py +15 -17
  30. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/odin/_odin_io.py +1 -1
  31. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/pmac/_pmac_io.py +23 -4
  32. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/pmac/_pmac_trajectory.py +47 -10
  33. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/eiger/_eiger_io.py +20 -1
  34. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/jungfrau/_signals.py +4 -1
  35. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/_block.py +28 -6
  36. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/_writer.py +1 -3
  37. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/core/_tango_transport.py +7 -17
  38. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/demo/_counter.py +2 -2
  39. {ophyd_async-0.14.1 → ophyd_async-0.15/src/ophyd_async.egg-info}/PKG-INFO +1 -1
  40. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async.egg-info/SOURCES.txt +2 -1
  41. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/conftest.py +9 -4
  42. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/signal/test_signals.py +4 -4
  43. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests_tango/test_tango_signals.py +4 -4
  44. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests_tango/test_tango_transport.py +5 -10
  45. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_device.py +54 -3
  46. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_mock_signal_backend.py +25 -30
  47. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_multi_derived_signal.py +74 -12
  48. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_observe.py +10 -6
  49. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_single_derived_signal.py +33 -10
  50. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_soft_signal_backend.py +3 -3
  51. ophyd_async-0.15/tests/unit_tests/core/test_status.py +452 -0
  52. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_utils.py +144 -2
  53. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_writers.py +1 -1
  54. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/demo/test_epics_demo.py +9 -9
  55. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/eiger/test_odin_io.py +8 -10
  56. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/pmac/test_pmac_trajectory.py +54 -9
  57. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/pvi/test_pvi.py +17 -0
  58. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/test_motor.py +35 -3
  59. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/eiger/test_eiger_controller.py +6 -8
  60. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/eiger/test_eiger_detector.py +1 -1
  61. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/plan_stubs/test_settings.py +5 -5
  62. ophyd_async-0.15/tests/unit_tests/sim/__init__.py +0 -0
  63. ophyd_async-0.14.1/tests/unit_tests/core/test_status.py +0 -214
  64. {ophyd_async-0.14.1 → ophyd_async-0.15}/.codecov.yml +0 -0
  65. {ophyd_async-0.14.1 → ophyd_async-0.15}/.devcontainer/devcontainer.json +0 -0
  66. {ophyd_async-0.14.1 → ophyd_async-0.15}/.git-blame-ignore-revs +0 -0
  67. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  68. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  69. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  70. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/codeql/codeql-config.yml +0 -0
  71. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/pages/index.html +0 -0
  72. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/pages/make_switcher.py +0 -0
  73. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_codeql.yml +0 -0
  74. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_dist.yml +0 -0
  75. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_docs.yml +0 -0
  76. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_pypi.yml +0 -0
  77. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_release.yml +0 -0
  78. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_test.yml +0 -0
  79. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/_tox.yml +0 -0
  80. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/ci.yml +0 -0
  81. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/claude.yml +0 -0
  82. {ophyd_async-0.14.1 → ophyd_async-0.15}/.github/workflows/periodic.yml +0 -0
  83. {ophyd_async-0.14.1 → ophyd_async-0.15}/.gitignore +0 -0
  84. {ophyd_async-0.14.1 → ophyd_async-0.15}/.gitleaks.toml +0 -0
  85. {ophyd_async-0.14.1 → ophyd_async-0.15}/.gitmodules +0 -0
  86. {ophyd_async-0.14.1 → ophyd_async-0.15}/.pre-commit-config.yaml +0 -0
  87. {ophyd_async-0.14.1 → ophyd_async-0.15}/.python-version +0 -0
  88. {ophyd_async-0.14.1 → ophyd_async-0.15}/Dockerfile +0 -0
  89. {ophyd_async-0.14.1 → ophyd_async-0.15}/LICENSE +0 -0
  90. {ophyd_async-0.14.1 → ophyd_async-0.15}/README.md +0 -0
  91. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/_static/custom.css +0 -0
  92. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
  93. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
  94. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
  95. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
  96. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
  97. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
  98. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0007-subpackage-structure.md +0 -0
  99. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0008-signal-types.md +0 -0
  100. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0009-procedural-vs-declarative-devices.md +0 -0
  101. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0010-docstring-format.md +0 -0
  102. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/0011-buffer-updates-camonitor.md +0 -0
  103. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions/COPYME +0 -0
  104. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/decisions.md +0 -0
  105. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/declarative-vs-procedural.md +0 -0
  106. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/design-goals.md +0 -0
  107. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/device-connection-strategies.md +0 -0
  108. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/devices-signals-backends.md +0 -0
  109. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/fly-scanning.md +0 -0
  110. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/plan-stubs.md +0 -0
  111. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/when-to-extend-movable.md +0 -0
  112. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations/where-device-logic.md +0 -0
  113. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/explanations.md +0 -0
  114. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/genindex.rst +0 -0
  115. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/choose-right-baseclass.md +0 -0
  116. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/contribute.md +0 -0
  117. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/derive-one-signal-from-others.md +0 -0
  118. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/implement-ad-detector.md +0 -0
  119. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/put-device-back.md +0 -0
  120. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to/store-and-retrieve.md +0 -0
  121. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/how-to.md +0 -0
  122. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/images/fly_scan_collection_windows_and_frames.svg +0 -0
  123. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/images/ophyd-async-logo.svg +0 -0
  124. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/images/ophyd-favicon.svg +0 -0
  125. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/images/set_and_wait_for_other_value.excalidraw.svg +0 -0
  126. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/index.md +0 -0
  127. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/reference.md +0 -0
  128. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/tutorials/implementing-detectors.md +0 -0
  129. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/tutorials/implementing-devices.md +0 -0
  130. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/tutorials/installation.md +0 -0
  131. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/tutorials/using-devices.md +0 -0
  132. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/tutorials/writing-tests-for-devices.md +0 -0
  133. {ophyd_async-0.14.1 → ophyd_async-0.15}/docs/tutorials.md +0 -0
  134. {ophyd_async-0.14.1 → ophyd_async-0.15}/renovate.json +0 -0
  135. {ophyd_async-0.14.1 → ophyd_async-0.15}/setup.cfg +0 -0
  136. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/__init__.py +0 -0
  137. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/__main__.py +0 -0
  138. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/_docs_parser.py +0 -0
  139. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_detector.py +0 -0
  140. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_device.py +0 -0
  141. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_enums.py +0 -0
  142. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_flyer.py +0 -0
  143. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_hdf_dataset.py +0 -0
  144. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_log.py +0 -0
  145. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_protocol.py +0 -0
  146. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_providers.py +0 -0
  147. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_readable.py +0 -0
  148. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_settings.py +0 -0
  149. /ophyd_async-0.14.1/src/ophyd_async/plan_stubs/_fly.py → /ophyd_async-0.15/src/ophyd_async/core/_typing.py +0 -0
  150. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/core/_yaml_settings.py +0 -0
  151. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/__init__.py +0 -0
  152. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adandor/__init__.py +0 -0
  153. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adandor/_andor.py +0 -0
  154. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adandor/_andor_controller.py +0 -0
  155. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adandor/_andor_io.py +0 -0
  156. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adaravis/__init__.py +0 -0
  157. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adaravis/_aravis.py +0 -0
  158. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adaravis/_aravis_controller.py +0 -0
  159. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adaravis/_aravis_io.py +0 -0
  160. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/__init__.py +0 -0
  161. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_core_detector.py +0 -0
  162. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_core_writer.py +0 -0
  163. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_hdf_writer.py +0 -0
  164. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_jpeg_writer.py +0 -0
  165. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_single_trigger.py +0 -0
  166. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_tiff_writer.py +0 -0
  167. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adcore/_utils.py +0 -0
  168. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adkinetix/__init__.py +0 -0
  169. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adkinetix/_kinetix.py +0 -0
  170. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adkinetix/_kinetix_controller.py +0 -0
  171. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adkinetix/_kinetix_io.py +0 -0
  172. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adpilatus/__init__.py +0 -0
  173. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adpilatus/_pilatus.py +0 -0
  174. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adpilatus/_pilatus_controller.py +0 -0
  175. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adpilatus/_pilatus_io.py +0 -0
  176. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adsimdetector/__init__.py +0 -0
  177. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adsimdetector/_sim.py +0 -0
  178. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adsimdetector/_sim_controller.py +0 -0
  179. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/adsimdetector/_sim_io.py +0 -0
  180. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/advimba/__init__.py +0 -0
  181. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/advimba/_vimba.py +0 -0
  182. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/advimba/_vimba_controller.py +0 -0
  183. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/advimba/_vimba_io.py +0 -0
  184. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_epics_device.py +0 -0
  185. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/core/_pvi_connector.py +0 -0
  186. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/__init__.py +0 -0
  187. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/__main__.py +0 -0
  188. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/_ioc.py +0 -0
  189. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/_point_detector.py +0 -0
  190. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/_point_detector_channel.py +0 -0
  191. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/_stage.py +0 -0
  192. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/motor.db +0 -0
  193. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/point_detector.db +0 -0
  194. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/demo/point_detector_channel.db +0 -0
  195. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/odin/__init__.py +0 -0
  196. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/pmac/__init__.py +0 -0
  197. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/pmac/_pmac_trajectory_generation.py +0 -0
  198. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/pmac/_utils.py +0 -0
  199. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/signal.py +0 -0
  200. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/testing/__init__.py +0 -0
  201. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/testing/_example_ioc.py +0 -0
  202. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/testing/_utils.py +0 -0
  203. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/testing/test_records.db +0 -0
  204. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/epics/testing/test_records_pva.db +0 -0
  205. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/__init__.py +0 -0
  206. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/core.py +0 -0
  207. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/eiger/__init__.py +0 -0
  208. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/eiger/_eiger.py +0 -0
  209. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/eiger/_eiger_controller.py +0 -0
  210. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/jungfrau/__init__.py +0 -0
  211. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/jungfrau/_controller.py +0 -0
  212. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/jungfrau/_jungfrau.py +0 -0
  213. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/jungfrau/_utils.py +0 -0
  214. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/odin/__init__.py +0 -0
  215. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/__init__.py +0 -0
  216. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/_control.py +0 -0
  217. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/_hdf_panda.py +0 -0
  218. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/_table.py +0 -0
  219. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/fastcs/panda/_trigger.py +0 -0
  220. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/__init__.py +0 -0
  221. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/_ensure_connected.py +0 -0
  222. /ophyd_async-0.14.1/src/ophyd_async/py.typed → /ophyd_async-0.15/src/ophyd_async/plan_stubs/_fly.py +0 -0
  223. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/_nd_attributes.py +0 -0
  224. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/_panda.py +0 -0
  225. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/_settings.py +0 -0
  226. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/_utils.py +0 -0
  227. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/plan_stubs/_wait_for_awaitable.py +0 -0
  228. /ophyd_async-0.14.1/tests/system_tests/__init__.py → /ophyd_async-0.15/src/ophyd_async/py.typed +0 -0
  229. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/__init__.py +0 -0
  230. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/__main__.py +0 -0
  231. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_blob_detector.py +0 -0
  232. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_blob_detector_controller.py +0 -0
  233. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_blob_detector_writer.py +0 -0
  234. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_mirror_horizontal.py +0 -0
  235. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_mirror_vertical.py +0 -0
  236. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_motor.py +0 -0
  237. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_pattern_generator.py +0 -0
  238. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_point_detector.py +0 -0
  239. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/sim/_stage.py +0 -0
  240. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/__init__.py +0 -0
  241. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/core/__init__.py +0 -0
  242. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/core/_base_device.py +0 -0
  243. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/core/_converters.py +0 -0
  244. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/core/_signal.py +0 -0
  245. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/core/_utils.py +0 -0
  246. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/demo/__init__.py +0 -0
  247. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/demo/_detector.py +0 -0
  248. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/demo/_mover.py +0 -0
  249. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/demo/_tango/__init__.py +0 -0
  250. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/demo/_tango/_servers.py +0 -0
  251. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/testing/__init__.py +0 -0
  252. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/testing/_one_of_everything.py +0 -0
  253. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/tango/testing/_test_config.py +0 -0
  254. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/__init__.py +0 -0
  255. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/__pytest_assert_rewrite.py +0 -0
  256. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/_assert.py +0 -0
  257. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/_one_of_everything.py +0 -0
  258. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/_single_derived.py +0 -0
  259. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/_utils.py +0 -0
  260. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async/testing/_wait_for_pending.py +0 -0
  261. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
  262. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async.egg-info/requires.txt +0 -0
  263. {ophyd_async-0.14.1 → ophyd_async-0.15}/src/ophyd_async.egg-info/top_level.txt +0 -0
  264. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/README.md +0 -0
  265. {ophyd_async-0.14.1/tests/unit_tests → ophyd_async-0.15/tests/system_tests}/__init__.py +0 -0
  266. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/conftest.py +0 -0
  267. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/adsim/baseline.yaml +0 -0
  268. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/adsim/external_dependencies.sh +0 -0
  269. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/adsim/test_adsim_system.py +0 -0
  270. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/eiger/README.md +0 -0
  271. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/eiger/start_iocs_and_run_tests.sh +0 -0
  272. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/eiger/test_eiger_system.py +0 -0
  273. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/signal/test_yaml_save_ca.yaml +0 -0
  274. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/epics/signal/test_yaml_save_pva.yaml +0 -0
  275. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/fastcs/panda/test_panda_connect.py +0 -0
  276. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests/test_tutorials.py +0 -0
  277. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests_tango/conftest.py +0 -0
  278. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests_tango/context_subprocess.py +0 -0
  279. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/system_tests_tango/test_base_device.py +0 -0
  280. {ophyd_async-0.14.1/tests/unit_tests/fastcs/jungfrau → ophyd_async-0.15/tests/unit_tests}/__init__.py +0 -0
  281. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_auto_init_devices.py +0 -0
  282. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_detector.py +0 -0
  283. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_flyer.py +0 -0
  284. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_log.py +0 -0
  285. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_protocol.py +0 -0
  286. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_providers.py +0 -0
  287. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_readable.py +0 -0
  288. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_signal.py +0 -0
  289. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_subset_enum.py +0 -0
  290. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_table.py +0 -0
  291. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/core/test_watchable_async_status.py +0 -0
  292. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adandor/test_andor.py +0 -0
  293. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adaravis/test_aravis.py +0 -0
  294. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_cont_acq_detector.py +0 -0
  295. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_detectors.py +0 -0
  296. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_drivers.py +0 -0
  297. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_plugins.py +0 -0
  298. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_scans.py +0 -0
  299. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adcore/test_single_trigger.py +0 -0
  300. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adkinetix/test_kinetix.py +0 -0
  301. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adpilatus/test_pilatus.py +0 -0
  302. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/adsimdetector/test_sim.py +0 -0
  303. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/advimba/test_vimba.py +0 -0
  304. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/conftest.py +0 -0
  305. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/pmac/conftest.py +0 -0
  306. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/pmac/test_pmac_io.py +0 -0
  307. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/pmac/test_pmac_trajectory_generation.py +0 -0
  308. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/pmac/test_pmac_utils.py +0 -0
  309. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/signal/test_common.py +0 -0
  310. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/epics/test_areadetector_subclass_naming.py +0 -0
  311. {ophyd_async-0.14.1/tests/unit_tests/sim → ophyd_async-0.15/tests/unit_tests/fastcs/jungfrau}/__init__.py +0 -0
  312. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/jungfrau/test_controller.py +0 -0
  313. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/jungfrau/test_utils.py +0 -0
  314. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/db/panda.db +0 -0
  315. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_hdf_panda.py +0 -0
  316. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_panda_connect_mock.py +0 -0
  317. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_panda_control.py +0 -0
  318. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_panda_utils.py +0 -0
  319. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_seq_table.py +0 -0
  320. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_trigger.py +0 -0
  321. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/fastcs/panda/test_writer.py +0 -0
  322. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/plan_stubs/test_ensure_connected.py +0 -0
  323. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/plan_stubs/test_fly.py +0 -0
  324. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/plan_stubs/test_setup.py +0 -0
  325. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/sim/test_sim_blob_detector.py +0 -0
  326. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/sim/test_sim_motor.py +0 -0
  327. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/test_branching.py +0 -0
  328. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/test_cli.py +0 -0
  329. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/test_data/test_yaml_config_save.yaml +0 -0
  330. {ophyd_async-0.14.1 → ophyd_async-0.15}/tests/unit_tests/test_data/test_yaml_save.yaml +0 -0
  331. {ophyd_async-0.14.1 → ophyd_async-0.15}/uv.lock +0 -0
@@ -1,5 +1,5 @@
1
1
  # Changes here will be overwritten by Copier
2
- _commit: 5.0.1
2
+ _commit: 5.0.2
3
3
  _src_path: https://github.com/DiamondLightSource/python-copier-template
4
4
  author_email: tom.cobb@diamond.ac.uk
5
5
  author_name: Tom Cobb
@@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua
24
24
 
25
25
  This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects.
26
26
 
27
- For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/5.0.1/how-to.html).
27
+ For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/5.0.2/how-to.html).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ophyd-async
3
- Version: 0.14.1
3
+ Version: 0.15
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
@@ -147,6 +147,7 @@ obj_ignore = [
147
147
  "ophyd_async.core._device_filler.SignalBackendT",
148
148
  "ophyd_async.core._device_filler.DeviceConnectorT",
149
149
  "ophyd_async.core._derived_signal_backend.TransformT",
150
+ "ophyd_async.core._mock_signal_backend.MockPutCallback",
150
151
  "ophyd_async.core._protocol.C",
151
152
  "ophyd_async.core._signal_backend.SignalDatatypeV",
152
153
  "ophyd_async.core._status.AsyncStatusBase",
@@ -164,8 +165,10 @@ obj_ignore = [
164
165
  "ophyd_async.testing._utils.T",
165
166
  "ophyd_async.sim._mirror.TwoJackRaw",
166
167
  "ophyd_async.sim._mirror.TwoJackDerived",
168
+ "non_zero",
167
169
  "0.1",
168
170
  "1.0",
171
+ "bluesky.protocols.T_co",
169
172
  ]
170
173
  nitpick_ignore = []
171
174
  for var in obj_ignore:
@@ -43,16 +43,9 @@ To observe every value change and run a function on that value you can use [](#o
43
43
  async for value in observe_value(signal):
44
44
  do_something_with(value)
45
45
  ```
46
- This will run until you `break` out of the loop. If you would like to break out of the loop when some other operation is complete you can pass an [](#AsyncStatus) that you create yourself or from the result of setting a signal, which will break out of the loop:
46
+ This will run until you `break` out of the loop.
47
47
 
48
- ```python
49
- status = signal.set(value)
50
- async for value in observe_value(signal2, done_status=status):
51
- do_something_with(value)
52
- # when signal.set() completes the loop will break out here
53
- ```
54
-
55
- You can pass `timeout` to specify how the maximum time to wait for a single update, and `done_timeout` to specify the maximum time to wait for `done_status`.
48
+ You can pass `timeout` to specify the maximum time to wait for a single update.
56
49
 
57
50
  If you want to wait for multiple signals you can use [](#observe_signals_value):
58
51
  ```python
@@ -63,6 +56,38 @@ async for signal, value in observe_value(signal1, signal2):
63
56
  do_something_else_with(value)
64
57
  ```
65
58
 
59
+ ## Use AsyncStatus as a context manager to bound loop execution
60
+
61
+ If you want a loop to run until some operation completes, you can use [](#AsyncStatus) as a context manager. When the status completes, it will cancel the calling task, causing the loop to exit. This is useful when you want to process signal updates until another operation finishes:
62
+
63
+ ```python
64
+ # Process updates while a motor is moving
65
+ async with motor.set(target_position):
66
+ async for value in observe_value(detector):
67
+ process_reading(value)
68
+ # Loop automatically exits when motor reaches position
69
+ ```
70
+
71
+ If the loop completes before the status, the status task is automatically cancelled:
72
+
73
+ ```python
74
+ async with signal1.set(new_value):
75
+ for i in range(3):
76
+ value = await signal.get_value()
77
+ process(value)
78
+ # Loop completes after 3 iterations, cancelling the wait for signal1 to finishe being set
79
+ ```
80
+
81
+ If an exception is raised in the loop body, it propagates out normally:
82
+
83
+ ```python
84
+ async with signal1.set(new_value):
85
+ async for value in observe_value(signal2):
86
+ if value > threshold:
87
+ raise ValueError("Threshold exceeded")
88
+ # Exception propagates, status is cancelled and no longer waits for signal1 to finish being set
89
+ ```
90
+
66
91
  ## Wait for the value to match some expected value
67
92
 
68
93
  If you don't need to run code for every signal update, but just want to wait until the signal matches some expected value, you can use [](#wait_for_value):
@@ -147,7 +147,7 @@ commands = [
147
147
  [
148
148
  "pyright",
149
149
  "--pythonpath",
150
- ".venv/bin/python",
150
+ "{env:VIRTUAL_ENV}/bin/python",
151
151
  "src",
152
152
  { replace = "posargs", default = [
153
153
  ], extend = true },
@@ -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.1'
32
- __version_tuple__ = version_tuple = (0, 14, 1)
31
+ __version__ = version = '0.15'
32
+ __version_tuple__ = version_tuple = (0, 15)
33
33
 
34
- __commit_id__ = commit_id = 'g9b567b214'
34
+ __commit_id__ = commit_id = 'g6ebd5ae5e'
@@ -1,5 +1,6 @@
1
1
  """The building blocks for making devices."""
2
2
 
3
+ from ._datatypes import Array1D, DTypeScalar_co, Table, TableSubclass
3
4
  from ._derived_signal import (
4
5
  DerivedSignalFactory,
5
6
  derived_signal_r,
@@ -23,7 +24,7 @@ from ._device import (
23
24
  default_mock_class,
24
25
  init_devices,
25
26
  )
26
- from ._device_filler import DeviceFiller
27
+ from ._device_filler import DeviceAnnotation, DeviceFiller
27
28
  from ._enums import (
28
29
  EnabledDisabled,
29
30
  EnableDisable,
@@ -44,7 +45,15 @@ from ._mock_signal_utils import (
44
45
  set_mock_value,
45
46
  set_mock_values,
46
47
  )
47
- from ._protocol import AsyncConfigurable, AsyncReadable, AsyncStageable, Watcher
48
+ from ._protocol import (
49
+ AsyncConfigurable,
50
+ AsyncLocatable,
51
+ AsyncMovable,
52
+ AsyncPausable,
53
+ AsyncReadable,
54
+ AsyncStageable,
55
+ Watcher,
56
+ )
48
57
  from ._providers import (
49
58
  AutoIncrementFilenameProvider,
50
59
  AutoIncrementingPathProvider,
@@ -86,8 +95,6 @@ from ._signal import (
86
95
  walk_signal_sources,
87
96
  )
88
97
  from ._signal_backend import (
89
- Array1D,
90
- DTypeScalar_co,
91
98
  Primitive,
92
99
  SignalBackend,
93
100
  SignalDatatype,
@@ -97,7 +104,6 @@ from ._signal_backend import (
97
104
  )
98
105
  from ._soft_signal_backend import SoftSignalBackend
99
106
  from ._status import AsyncStatus, WatchableAsyncStatus, completed_status
100
- from ._table import Table, TableSubclass
101
107
  from ._utils import (
102
108
  CALCULATE_TIMEOUT,
103
109
  DEFAULT_TIMEOUT,
@@ -117,6 +123,7 @@ from ._utils import (
117
123
  get_enum_cls,
118
124
  get_unique,
119
125
  in_micros,
126
+ non_zero,
120
127
  wait_for_connection,
121
128
  )
122
129
  from ._yaml_settings import YamlSettingsProvider
@@ -146,11 +153,15 @@ __all__ = [
146
153
  "Device",
147
154
  "DeviceConnector",
148
155
  "DeviceFiller",
156
+ "DeviceAnnotation",
149
157
  "DeviceVector",
150
158
  "init_devices",
151
159
  # Protocols
152
160
  "AsyncReadable",
153
161
  "AsyncConfigurable",
162
+ "AsyncLocatable",
163
+ "AsyncMovable",
164
+ "AsyncPausable",
154
165
  "AsyncStageable",
155
166
  "Watcher",
156
167
  # Status
@@ -255,6 +266,7 @@ __all__ = [
255
266
  "make_datakey",
256
267
  "wait_for_connection",
257
268
  "Ignore",
269
+ "non_zero",
258
270
  # Derived signal
259
271
  "derived_signal_r",
260
272
  "derived_signal_rw",
@@ -1,13 +1,25 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable, Sequence
4
- from typing import Annotated, Any, TypeVar, get_origin, get_type_hints
4
+ from typing import Annotated, Any, TypeVar
5
5
 
6
6
  import numpy as np
7
7
  from pydantic import ConfigDict, Field, model_validator
8
8
  from pydantic_numpy.helper.annotation import NpArrayPydanticAnnotation
9
9
 
10
- from ._utils import ConfinedModel, get_dtype
10
+ from ._utils import ConfinedModel, cached_get_origin, cached_get_type_hints, get_dtype
11
+
12
+ DTypeScalar_co = TypeVar("DTypeScalar_co", covariant=True, bound=np.generic)
13
+ """A numpy dtype like [](#numpy.float64)."""
14
+
15
+
16
+ # To be a 1D array shape should really be tuple[int], but np.array()
17
+ # currently produces tuple[int, ...] even when it has 1D input args
18
+ # https://github.com/numpy/numpy/issues/28077#issuecomment-2566485178
19
+ Array1D = np.ndarray[tuple[int, ...], np.dtype[DTypeScalar_co]]
20
+ """A type alias for a 1D numpy array with a specific scalar data type.
21
+
22
+ E.g. `Array1D[np.float64]` is a 1D numpy array of 64-bit floats."""
11
23
 
12
24
  TableSubclass = TypeVar("TableSubclass", bound="Table")
13
25
 
@@ -75,11 +87,8 @@ class Table(ConfinedModel):
75
87
  # ...but forbid extra in subclasses so it gets validated
76
88
  cls.model_config = ConfigDict(validate_assignment=True, extra="forbid")
77
89
  # Change fields to have the correct annotations
78
- # TODO: refactor so we don't need this to break circular imports
79
- from ._signal_backend import Array1D
80
-
81
- for k, anno in get_type_hints(cls, localns={"Array1D": Array1D}).items():
82
- if get_origin(anno) is np.ndarray:
90
+ for k, anno in cached_get_type_hints(cls).items():
91
+ if cached_get_origin(anno) is np.ndarray:
83
92
  dtype = get_dtype(anno)
84
93
  new_anno = Annotated[
85
94
  anno,
@@ -88,7 +97,7 @@ class Table(ConfinedModel):
88
97
  ),
89
98
  Field(default_factory=_make_default_factory(dtype)),
90
99
  ]
91
- elif get_origin(anno) is Sequence:
100
+ elif cached_get_origin(anno) is Sequence:
92
101
  new_anno = Annotated[anno, Field(default_factory=list)]
93
102
  else:
94
103
  raise TypeError(f"Cannot use annotation {anno} in a Table")
@@ -156,7 +165,7 @@ class Table(ConfinedModel):
156
165
  raise AssertionError(f"Cannot construct Table from {data}")
157
166
  for field_name, field_value in cls.model_fields.items():
158
167
  if (
159
- get_origin(field_value.annotation) is np.ndarray
168
+ cached_get_origin(field_value.annotation) is np.ndarray
160
169
  and field_value.annotation
161
170
  and field_name in data_dict
162
171
  ):
@@ -1,5 +1,14 @@
1
- from collections.abc import Awaitable, Callable
2
- from typing import Any, Generic, get_args, get_origin, get_type_hints, is_typeddict
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
+ is_typeddict,
11
+ )
3
12
 
4
13
  from bluesky.protocols import Locatable
5
14
 
@@ -12,6 +21,7 @@ from ._derived_signal_backend import (
12
21
  from ._device import Device
13
22
  from ._signal import Signal, SignalR, SignalRW, SignalT, SignalW
14
23
  from ._signal_backend import Primitive, SignalDatatypeT
24
+ from ._utils import cached_get_type_hints
15
25
 
16
26
 
17
27
  class DerivedSignalFactory(Generic[TransformT]):
@@ -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
- raise TypeError(msg)
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
  )
@@ -185,7 +208,7 @@ class DerivedSignalFactory(Generic[TransformT]):
185
208
 
186
209
 
187
210
  def _get_return_datatype(func: Callable[..., SignalDatatypeT]) -> type[SignalDatatypeT]:
188
- args = get_type_hints(func)
211
+ args = cached_get_type_hints(func)
189
212
  if "return" not in args:
190
213
  msg = f"{func} does not have a type hint for it's return value"
191
214
  raise TypeError(msg)
@@ -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 = get_type_hints(func)
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
- raw_to_derived: Callable[..., SignalDatatypeT] | None = None,
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 raw_to_derived:
239
+ if raw_to_derived_func:
212
240
 
213
241
  class DerivedTransform(Transform):
214
- def raw_to_derived(self, **kwargs) -> dict[str, SignalDatatypeT]:
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
- raw_to_derived=raw_to_derived,
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
- set_derived_datatype = _get_first_arg_datatype(set_derived)
282
- if raw_to_derived_datatype != set_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"!= {set_derived_datatype} datatype {set_derived_datatype}"
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
- raw_to_derived=raw_to_derived,
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
@@ -291,11 +291,7 @@ class DerivedSignalBackend(SignalBackend[SignalDatatypeT]):
291
291
  )
292
292
  raise RuntimeError(msg)
293
293
 
294
- async def put(self, value: SignalDatatypeT | None, wait: bool) -> None:
295
- if wait is False:
296
- msg = "Cannot put with wait=False"
297
- raise RuntimeError(msg)
298
-
294
+ async def put(self, value: SignalDatatypeT | None) -> None:
299
295
  value = error_if_none(
300
296
  value,
301
297
  "Must be given a value to put",
@@ -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,16 +10,16 @@ from typing import (
9
10
  NoReturn,
10
11
  Protocol,
11
12
  TypeVar,
13
+ Union,
12
14
  cast,
13
15
  get_args,
14
- get_type_hints,
15
16
  runtime_checkable,
16
17
  )
17
18
 
18
19
  from ._device import Device, DeviceConnector, DeviceVector
19
20
  from ._signal import Ignore, Signal, SignalX
20
21
  from ._signal_backend import SignalBackend, SignalDatatype
21
- from ._utils import get_origin_class
22
+ from ._utils import cached_get_origin, cached_get_type_hints, get_origin_class
22
23
 
23
24
  SignalBackendT = TypeVar("SignalBackendT", bound=SignalBackend)
24
25
  DeviceConnectorT = TypeVar("DeviceConnectorT", bound=DeviceConnector)
@@ -76,6 +77,7 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
76
77
  self._extras: dict[UniqueName, Sequence[Any]] = {}
77
78
  self._signal_datatype: dict[LogicalName, type | None] = {}
78
79
  self._vector_device_type: dict[LogicalName, type[Device] | None] = {}
80
+ self._optional_devices: set[str] = set()
79
81
  self.ignored_signals: set[str] = set()
80
82
  # Backends and Connectors stored ready for the connection phase
81
83
  self._unfilled_backends: dict[
@@ -113,14 +115,28 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
113
115
  # https://github.com/python/cpython/issues/124840
114
116
  cls = type(self._device)
115
117
  # Get hints without Annotated for determining types
116
- hints = get_type_hints(cls)
118
+ hints = cached_get_type_hints(cls)
117
119
  # Get hints with Annotated for wrapping signals and backends
118
- extra_hints = get_type_hints(cls, include_extras=True)
120
+ extra_hints = cached_get_type_hints(cls, include_extras=True)
119
121
  for attr_name, annotation in hints.items():
120
122
  if annotation is Ignore:
121
123
  self.ignored_signals.add(attr_name)
122
124
  name = UniqueName(attr_name)
123
125
  origin = get_origin_class(annotation)
126
+ args = get_args(annotation)
127
+
128
+ if (
129
+ cached_get_origin(annotation) is Union
130
+ and types.NoneType in args
131
+ and len(args) == 2
132
+ ):
133
+ # Annotation is an Union with two arguments, one of which is None
134
+ # Make this signal an optional parameter and set origin to T
135
+ # so the device is added to unfilled_connectors
136
+ self._optional_devices.add(name)
137
+ (annotation,) = [x for x in args if x is not types.NoneType]
138
+ origin = get_origin_class(annotation)
139
+
124
140
  if (
125
141
  name == "parent"
126
142
  or name.startswith("_")
@@ -241,10 +257,17 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
241
257
  :param source: The source of the data that should have done the filling, for
242
258
  reporting as an error message
243
259
  """
244
- unfilled = sorted(set(self._unfilled_connectors).union(self._unfilled_backends))
245
- if unfilled:
260
+ unfilled = set(self._unfilled_connectors).union(self._unfilled_backends)
261
+ unfilled_optional = sorted(unfilled.intersection(self._optional_devices))
262
+
263
+ for name in unfilled_optional:
264
+ setattr(self._device, name, None)
265
+
266
+ required = sorted(unfilled.difference(unfilled_optional))
267
+
268
+ if required:
246
269
  raise RuntimeError(
247
- f"{self._device.name}: cannot provision {unfilled} from {source}"
270
+ f"{self._device.name}: cannot provision {required} from {source}"
248
271
  )
249
272
 
250
273
  def _ensure_device_vector(self, name: LogicalName) -> DeviceVector:
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- from collections.abc import Callable
4
+ from collections.abc import Awaitable, Callable
5
5
  from functools import cached_property
6
6
  from typing import TYPE_CHECKING
7
7
  from unittest.mock import AsyncMock
@@ -17,6 +17,11 @@ from ._utils import Callback
17
17
  if TYPE_CHECKING:
18
18
  from ._device import LazyMock
19
19
 
20
+ MockPutCallback = (
21
+ Callable[[SignalDatatypeT], SignalDatatypeT | None]
22
+ | Callable[[SignalDatatypeT], Awaitable[SignalDatatypeT | None]]
23
+ )
24
+
20
25
 
21
26
  class MockSignalBackend(SignalBackend[SignalDatatypeT]):
22
27
  """Signal backend for testing, created by ``Device.connect(mock=True)``."""
@@ -42,13 +47,27 @@ class MockSignalBackend(SignalBackend[SignalDatatypeT]):
42
47
 
43
48
  # use existing Mock if provided
44
49
  self.mock = mock
50
+ self._mock_put_callback: MockPutCallback | None = None
45
51
  super().__init__(datatype=self.initial_backend.datatype)
46
52
 
53
+ def set_mock_put_callback(self, callback: MockPutCallback | None):
54
+ if "put_mock" in self.__dict__:
55
+ # put_mock cached property exists, so set the side effect on it
56
+ self.put_mock.side_effect = callback
57
+ else:
58
+ # put_mock doesn't exist, don't create it as that would be slow
59
+ # so just keep it internally
60
+ self._mock_put_callback = callback
61
+
47
62
  @cached_property
48
63
  def put_mock(self) -> AsyncMock:
49
64
  """Return the mock that will track calls to `put()`."""
50
65
  put_mock = AsyncMock(
51
- name="put", spec=Callable, side_effect=lambda *_, **__: None
66
+ name="put",
67
+ spec=Callable,
68
+ side_effect=self._mock_put_callback
69
+ if self._mock_put_callback
70
+ else lambda v: None,
52
71
  )
53
72
  self.mock().attach_mock(put_mock, "put")
54
73
  return put_mock
@@ -73,13 +92,12 @@ class MockSignalBackend(SignalBackend[SignalDatatypeT]):
73
92
  put_proceeds.set()
74
93
  return put_proceeds
75
94
 
76
- async def put(self, value: SignalDatatypeT | None, wait: bool):
77
- new_value = await self.put_mock(value, wait=wait)
95
+ async def put(self, value: SignalDatatypeT | None):
96
+ new_value = await self.put_mock(value)
78
97
  if new_value is None:
79
98
  new_value = value
80
- await self.soft_backend.put(new_value, wait=wait)
81
- if wait:
82
- await self.put_proceeds.wait()
99
+ await self.soft_backend.put(new_value)
100
+ await self.put_proceeds.wait()
83
101
 
84
102
  async def get_reading(self) -> Reading:
85
103
  return await self.soft_backend.get_reading()
@@ -1,9 +1,9 @@
1
- from collections.abc import Awaitable, Callable, Iterable, Iterator
1
+ from collections.abc import Iterable, Iterator
2
2
  from contextlib import contextmanager
3
3
  from unittest.mock import AsyncMock, Mock
4
4
 
5
5
  from ._device import Device, DeviceMock
6
- from ._mock_signal_backend import MockSignalBackend
6
+ from ._mock_signal_backend import MockPutCallback, MockSignalBackend
7
7
  from ._signal import Signal, SignalConnector, SignalR
8
8
  from ._signal_backend import SignalDatatypeT
9
9
 
@@ -109,16 +109,12 @@ def set_mock_values(
109
109
 
110
110
 
111
111
  @contextmanager
112
- def _unset_side_effect_cm(put_mock: AsyncMock):
112
+ def _unset_side_effect_cm(backend: MockSignalBackend):
113
113
  yield
114
- put_mock.side_effect = None
114
+ backend.set_mock_put_callback(None)
115
115
 
116
116
 
117
- def callback_on_mock_put(
118
- signal: Signal[SignalDatatypeT],
119
- callback: Callable[[SignalDatatypeT, bool], SignalDatatypeT | None]
120
- | Callable[[SignalDatatypeT, bool], Awaitable[SignalDatatypeT | None]],
121
- ):
117
+ def callback_on_mock_put(signal: Signal[SignalDatatypeT], callback: MockPutCallback):
122
118
  """For setting a callback when a backend is put to.
123
119
 
124
120
  Can either be used in a context, with the callback being unset on exit, or
@@ -132,8 +128,8 @@ def callback_on_mock_put(
132
128
  context.
133
129
  """
134
130
  backend = _get_mock_signal_backend(signal)
135
- backend.put_mock.side_effect = callback
136
- return _unset_side_effect_cm(backend.put_mock)
131
+ backend.set_mock_put_callback(callback)
132
+ return _unset_side_effect_cm(backend)
137
133
 
138
134
 
139
135
  def set_mock_put_proceeds(signal: Signal, proceeds: bool):