ophyd-async 0.10.0a3__tar.gz → 0.10.0a4__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 (297) hide show
  1. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/PKG-INFO +1 -1
  2. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/pyproject.toml +0 -1
  3. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/_version.py +2 -2
  4. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/__init__.py +4 -0
  5. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_derived_signal.py +38 -10
  6. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_detector.py +4 -4
  7. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_signal.py +21 -5
  8. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_signal_backend.py +18 -8
  9. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_utils.py +31 -14
  10. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_core_detector.py +2 -2
  11. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_core_io.py +3 -3
  12. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_utils.py +11 -2
  13. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adpilatus/_pilatus.py +1 -1
  14. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adpilatus/_pilatus_controller.py +4 -11
  15. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_aioca.py +2 -2
  16. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_util.py +21 -13
  17. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/eiger/_odin_io.py +12 -13
  18. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/motor.py +3 -2
  19. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/eiger/_eiger.py +11 -2
  20. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/eiger/_eiger_controller.py +7 -3
  21. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/eiger/_eiger_io.py +1 -0
  22. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_motor.py +2 -2
  23. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/_base_device.py +2 -1
  24. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/_converters.py +2 -6
  25. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/demo/_tango/_servers.py +0 -1
  26. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async.egg-info/PKG-INFO +1 -1
  27. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_multi_derived_signal.py +5 -2
  28. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_observe.py +56 -0
  29. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_single_derived_signal.py +31 -16
  30. ophyd_async-0.10.0a4/tests/epics/adandor/test_andor.py +37 -0
  31. ophyd_async-0.10.0a4/tests/epics/adaravis/test_aravis.py +46 -0
  32. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adpilatus/test_pilatus.py +38 -49
  33. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/conftest.py +1 -0
  34. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/eiger/test_odin_io.py +22 -13
  35. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/signal/test_common.py +39 -1
  36. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/test_motor.py +15 -3
  37. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/eiger/test_eiger_controller.py +7 -1
  38. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/eiger/test_eiger_detector.py +20 -0
  39. ophyd_async-0.10.0a3/tests/epics/adandor/test_andor.py +0 -46
  40. ophyd_async-0.10.0a3/tests/epics/adaravis/test_aravis.py +0 -44
  41. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.codecov.yml +0 -0
  42. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.copier-answers.yml +0 -0
  43. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.devcontainer/devcontainer.json +0 -0
  44. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.git-blame-ignore-revs +0 -0
  45. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/CONTRIBUTING.md +0 -0
  46. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  47. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  48. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  49. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/actions/install_requirements/action.yml +0 -0
  50. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/codeql/codeql-config.yml +0 -0
  51. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/dependabot.yml +0 -0
  52. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/pages/index.html +0 -0
  53. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/pages/make_switcher.py +0 -0
  54. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_check.yml +0 -0
  55. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_codeql.yml +0 -0
  56. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_dist.yml +0 -0
  57. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_docs.yml +0 -0
  58. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_import_with_no_extras.yml +0 -0
  59. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_pypi.yml +0 -0
  60. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_release.yml +0 -0
  61. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_test.yml +0 -0
  62. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/_tox.yml +0 -0
  63. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/ci.yml +0 -0
  64. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.github/workflows/periodic.yml +0 -0
  65. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.gitignore +0 -0
  66. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/.pre-commit-config.yaml +0 -0
  67. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/Dockerfile +0 -0
  68. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/LICENSE +0 -0
  69. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/README.md +0 -0
  70. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/_static/custom.css +0 -0
  71. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/conf.py +0 -0
  72. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
  73. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
  74. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
  75. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
  76. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
  77. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
  78. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0007-subpackage-structure.md +0 -0
  79. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0008-signal-types.md +0 -0
  80. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0009-procedural-vs-declarative-devices.md +0 -0
  81. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/0010-docstring-format.md +0 -0
  82. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions/COPYME +0 -0
  83. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/decisions.md +0 -0
  84. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/declarative-vs-procedural.md +0 -0
  85. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/design-goals.md +0 -0
  86. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/device-connection-strategies.md +0 -0
  87. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/devices-signals-backends.md +0 -0
  88. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/flyscanning.md +0 -0
  89. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations/where-device-logic.md +0 -0
  90. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/explanations.md +0 -0
  91. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/genindex.rst +0 -0
  92. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/choose-right-baseclass.md +0 -0
  93. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/contribute.md +0 -0
  94. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/derive-one-signal-from-others.md +0 -0
  95. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/implement-ad-detector.md +0 -0
  96. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/interact-with-signals.md +0 -0
  97. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/put-device-back.md +0 -0
  98. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to/store-and-retrieve.md +0 -0
  99. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/how-to.md +0 -0
  100. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/images/flyscan_collection_windows_and_frames.svg +0 -0
  101. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/images/ophyd-async-logo.svg +0 -0
  102. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/images/ophyd-favicon.svg +0 -0
  103. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/images/set_and_wait_for_other_value.excalidraw.svg +0 -0
  104. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/index.md +0 -0
  105. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/reference.md +0 -0
  106. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/tutorials/implementing-detectors.md +0 -0
  107. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/tutorials/implementing-devices.md +0 -0
  108. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/tutorials/installation.md +0 -0
  109. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/tutorials/using-devices.md +0 -0
  110. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/tutorials/writing-tests-for-devices.md +0 -0
  111. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/docs/tutorials.md +0 -0
  112. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/setup.cfg +0 -0
  113. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/__init__.py +0 -0
  114. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/__main__.py +0 -0
  115. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/_docs_parser.py +0 -0
  116. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_derived_signal_backend.py +0 -0
  117. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_device.py +0 -0
  118. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_device_filler.py +0 -0
  119. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_flyer.py +0 -0
  120. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_hdf_dataset.py +0 -0
  121. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_log.py +0 -0
  122. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_mock_signal_backend.py +0 -0
  123. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_protocol.py +0 -0
  124. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_providers.py +0 -0
  125. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_readable.py +0 -0
  126. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_settings.py +0 -0
  127. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_soft_signal_backend.py +0 -0
  128. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_status.py +0 -0
  129. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_table.py +0 -0
  130. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/core/_yaml_settings.py +0 -0
  131. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/__init__.py +0 -0
  132. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adandor/__init__.py +0 -0
  133. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adandor/_andor.py +0 -0
  134. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adandor/_andor_controller.py +0 -0
  135. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adandor/_andor_io.py +0 -0
  136. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adaravis/__init__.py +0 -0
  137. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adaravis/_aravis.py +0 -0
  138. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adaravis/_aravis_controller.py +0 -0
  139. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adaravis/_aravis_io.py +0 -0
  140. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/__init__.py +0 -0
  141. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_core_logic.py +0 -0
  142. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_core_writer.py +0 -0
  143. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_hdf_writer.py +0 -0
  144. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_jpeg_writer.py +0 -0
  145. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_single_trigger.py +0 -0
  146. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adcore/_tiff_writer.py +0 -0
  147. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adkinetix/__init__.py +0 -0
  148. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adkinetix/_kinetix.py +0 -0
  149. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adkinetix/_kinetix_controller.py +0 -0
  150. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adkinetix/_kinetix_io.py +0 -0
  151. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adpilatus/__init__.py +0 -0
  152. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adpilatus/_pilatus_io.py +0 -0
  153. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adsimdetector/__init__.py +0 -0
  154. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adsimdetector/_sim.py +0 -0
  155. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adsimdetector/_sim_controller.py +0 -0
  156. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/adsimdetector/_sim_io.py +0 -0
  157. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/advimba/__init__.py +0 -0
  158. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/advimba/_vimba.py +0 -0
  159. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/advimba/_vimba_controller.py +0 -0
  160. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/advimba/_vimba_io.py +0 -0
  161. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/__init__.py +0 -0
  162. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_epics_connector.py +0 -0
  163. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_epics_device.py +0 -0
  164. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_p4p.py +0 -0
  165. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_pvi_connector.py +0 -0
  166. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/core/_signal.py +0 -0
  167. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/__init__.py +0 -0
  168. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/__main__.py +0 -0
  169. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/_ioc.py +0 -0
  170. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/_motor.py +0 -0
  171. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/_point_detector.py +0 -0
  172. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/_point_detector_channel.py +0 -0
  173. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/_stage.py +0 -0
  174. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/motor.db +0 -0
  175. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/point_detector.db +0 -0
  176. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/demo/point_detector_channel.db +0 -0
  177. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/eiger/__init__.py +0 -0
  178. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/signal.py +0 -0
  179. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/testing/__init__.py +0 -0
  180. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/testing/_example_ioc.py +0 -0
  181. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/testing/_utils.py +0 -0
  182. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/testing/test_records.db +0 -0
  183. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/epics/testing/test_records_pva.db +0 -0
  184. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/__init__.py +0 -0
  185. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/core.py +0 -0
  186. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/eiger/__init__.py +0 -0
  187. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/odin/__init__.py +0 -0
  188. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/__init__.py +0 -0
  189. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/_block.py +0 -0
  190. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/_control.py +0 -0
  191. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/_hdf_panda.py +0 -0
  192. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/_table.py +0 -0
  193. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/_trigger.py +0 -0
  194. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/fastcs/panda/_writer.py +0 -0
  195. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/__init__.py +0 -0
  196. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_ensure_connected.py +0 -0
  197. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_fly.py +0 -0
  198. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_nd_attributes.py +0 -0
  199. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_panda.py +0 -0
  200. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_settings.py +0 -0
  201. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_utils.py +0 -0
  202. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/plan_stubs/_wait_for_awaitable.py +0 -0
  203. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/py.typed +0 -0
  204. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/__init__.py +0 -0
  205. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/__main__.py +0 -0
  206. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_blob_detector.py +0 -0
  207. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_blob_detector_controller.py +0 -0
  208. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_blob_detector_writer.py +0 -0
  209. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_mirror_horizontal.py +0 -0
  210. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_mirror_vertical.py +0 -0
  211. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_pattern_generator.py +0 -0
  212. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_point_detector.py +0 -0
  213. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/sim/_stage.py +0 -0
  214. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/__init__.py +0 -0
  215. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/__init__.py +0 -0
  216. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/_signal.py +8 -8
  217. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/_tango_readable.py +0 -0
  218. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/_tango_transport.py +12 -12
  219. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/core/_utils.py +0 -0
  220. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/demo/__init__.py +0 -0
  221. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/demo/_counter.py +0 -0
  222. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/demo/_detector.py +0 -0
  223. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/demo/_mover.py +0 -0
  224. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/demo/_tango/__init__.py +0 -0
  225. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/testing/__init__.py +0 -0
  226. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/tango/testing/_one_of_everything.py +2 -2
  227. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/__init__.py +0 -0
  228. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/__pytest_assert_rewrite.py +0 -0
  229. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/_assert.py +0 -0
  230. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/_mock_signal_utils.py +0 -0
  231. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/_one_of_everything.py +0 -0
  232. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/_single_derived.py +0 -0
  233. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/_utils.py +0 -0
  234. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async/testing/_wait_for_pending.py +0 -0
  235. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async.egg-info/SOURCES.txt +0 -0
  236. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
  237. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async.egg-info/requires.txt +0 -0
  238. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/src/ophyd_async.egg-info/top_level.txt +0 -0
  239. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/system_tests/epics/eiger/README.md +0 -0
  240. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/system_tests/epics/eiger/start_iocs_and_run_tests.sh +0 -0
  241. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/system_tests/epics/eiger/test_eiger_system.py +0 -0
  242. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/conftest.py +0 -0
  243. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_auto_init_devices.py +0 -0
  244. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_detector.py +0 -0
  245. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_device.py +0 -0
  246. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_flyer.py +0 -0
  247. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_log.py +0 -0
  248. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_mock_signal_backend.py +0 -0
  249. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_protocol.py +0 -0
  250. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_providers.py +0 -0
  251. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_readable.py +0 -0
  252. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_signal.py +0 -0
  253. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_soft_signal_backend.py +0 -0
  254. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_status.py +0 -0
  255. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_subset_enum.py +0 -0
  256. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_table.py +0 -0
  257. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_utils.py +0 -0
  258. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/core/test_watchable_async_status.py +0 -0
  259. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adcore/test_cont_acq_detector.py +0 -0
  260. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adcore/test_detectors.py +0 -0
  261. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adcore/test_drivers.py +0 -0
  262. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adcore/test_scans.py +0 -0
  263. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adcore/test_single_trigger.py +0 -0
  264. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adcore/test_writers.py +0 -0
  265. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adkinetix/test_kinetix.py +0 -0
  266. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/adsimdetector/test_sim.py +0 -0
  267. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/advimba/test_vimba.py +0 -0
  268. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/demo/test_epics_demo.py +0 -0
  269. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/pvi/test_pvi.py +0 -0
  270. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/signal/test_signals.py +0 -0
  271. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/signal/test_yaml_save_ca.yaml +0 -0
  272. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/signal/test_yaml_save_pva.yaml +0 -0
  273. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/epics/test_areadetector_subclass_naming.py +0 -0
  274. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/db/panda.db +0 -0
  275. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_hdf_panda.py +0 -0
  276. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_panda_connect.py +0 -0
  277. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_panda_control.py +0 -0
  278. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_panda_utils.py +0 -0
  279. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_seq_table.py +0 -0
  280. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_trigger.py +0 -0
  281. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/fastcs/panda/test_writer.py +0 -0
  282. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/plan_stubs/test_ensure_connected.py +0 -0
  283. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/plan_stubs/test_fly.py +0 -0
  284. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/plan_stubs/test_settings.py +0 -0
  285. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/plan_stubs/test_setup.py +0 -0
  286. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/sim/__init__.py +0 -0
  287. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/sim/test_sim_blob_detector.py +0 -0
  288. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/sim/test_sim_motor.py +0 -0
  289. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/tango/conftest.py +1 -1
  290. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/tango/context_subprocess.py +0 -0
  291. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/tango/test_base_device.py +10 -10
  292. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/tango/test_tango_signals.py +0 -0
  293. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/tango/test_tango_transport.py +9 -9
  294. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/test_cli.py +0 -0
  295. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/test_data/test_yaml_config_save.yaml +0 -0
  296. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/test_data/test_yaml_save.yaml +0 -0
  297. {ophyd_async-0.10.0a3 → ophyd_async-0.10.0a4}/tests/test_tutorials.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ophyd-async
3
- Version: 0.10.0a3
3
+ Version: 0.10.0a4
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
@@ -134,7 +134,6 @@ commands =
134
134
  """
135
135
 
136
136
  [tool.ruff]
137
- src = ["src", "tests", "system_tests"]
138
137
  line-length = 88
139
138
  lint.select = [
140
139
  "B", # flake8-bugbear - https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.10.0a3'
21
- __version_tuple__ = version_tuple = (0, 10, 0, 'a3')
20
+ __version__ = version = '0.10.0a4'
21
+ __version_tuple__ = version_tuple = (0, 10, 0, 'a4')
@@ -75,11 +75,13 @@ from ._utils import (
75
75
  DEFAULT_TIMEOUT,
76
76
  CalculatableTimeout,
77
77
  Callback,
78
+ EnumTypes,
78
79
  LazyMock,
79
80
  NotConnected,
80
81
  Reference,
81
82
  StrictEnum,
82
83
  SubsetEnum,
84
+ SupersetEnum,
83
85
  WatcherUpdate,
84
86
  gather_dict,
85
87
  get_dtype,
@@ -122,6 +124,8 @@ __all__ = [
122
124
  "Array1D",
123
125
  "StrictEnum",
124
126
  "SubsetEnum",
127
+ "SupersetEnum",
128
+ "EnumTypes",
125
129
  "Table",
126
130
  "SignalMetadata",
127
131
  # Soft signal
@@ -1,5 +1,7 @@
1
1
  from collections.abc import Awaitable, Callable
2
- from typing import Any, Generic, get_type_hints, is_typeddict
2
+ from typing import Any, Generic, get_args, get_origin, get_type_hints, is_typeddict
3
+
4
+ from bluesky.protocols import Locatable
3
5
 
4
6
  from ._derived_signal_backend import (
5
7
  DerivedSignalBackend,
@@ -8,7 +10,7 @@ from ._derived_signal_backend import (
8
10
  TransformT,
9
11
  )
10
12
  from ._device import Device
11
- from ._signal import SignalR, SignalRW, SignalT, SignalW
13
+ from ._signal import Signal, SignalR, SignalRW, SignalT, SignalW
12
14
  from ._signal_backend import SignalDatatypeT
13
15
 
14
16
 
@@ -35,15 +37,27 @@ class DerivedSignalFactory(Generic[TransformT]):
35
37
  self._set_derived = set_derived
36
38
  # Check the raw and transform devices match the input arguments of the Transform
37
39
  if transform_cls is not Transform:
38
- expected = list(transform_cls.model_fields) + [
39
- x
40
- for x in get_type_hints(transform_cls.raw_to_derived)
41
- if x not in ["self", "return"]
42
- ]
43
- if set(expected) != set(raw_and_transform_devices):
40
+ # Populate expected parameters and types
41
+ expected = {
42
+ **{k: f.annotation for k, f in transform_cls.model_fields.items()},
43
+ **{
44
+ k: v
45
+ for k, v in get_type_hints(transform_cls.raw_to_derived).items()
46
+ if k not in {"self", "return"}
47
+ },
48
+ }
49
+
50
+ # Populate received parameters and types
51
+ # Use Signal datatype, or Locatable datatype, or set type as None
52
+ received = {
53
+ k: v.datatype if isinstance(v, Signal) else get_locatable_type(v)
54
+ for k, v in raw_and_transform_devices.items()
55
+ }
56
+
57
+ if expected != received:
44
58
  msg = (
45
- f"Expected devices to be passed as keyword arguments {expected}, "
46
- f"got {list(raw_and_transform_devices)}"
59
+ f"Expected devices to be passed as keyword arguments "
60
+ f"{expected}, got {received}"
47
61
  )
48
62
  raise TypeError(msg)
49
63
  self._set_derived_takes_dict = (
@@ -269,3 +283,17 @@ def derived_signal_w(
269
283
  units=derived_units,
270
284
  precision=derived_precision,
271
285
  )
286
+
287
+
288
+ def get_locatable_type(obj: object) -> type | None:
289
+ """Extract datatype from Locatable parent class.
290
+
291
+ :param obj: Object with possible Locatable inheritance
292
+ :return: Type hint associated with Locatable, or None if not found.
293
+ """
294
+ for base in getattr(obj.__class__, "__orig_bases__", []):
295
+ if get_origin(base) is Locatable:
296
+ args = get_args(base)
297
+ if args:
298
+ return args[0]
299
+ return None
@@ -328,10 +328,10 @@ class StandardDetector(
328
328
  if isinstance(value.number_of_events, list)
329
329
  else [value.number_of_events]
330
330
  )
331
- self._describe, _ = await asyncio.gather(
332
- self._writer.open(self.name, value.exposures_per_event),
333
- self._controller.prepare(value),
334
- )
331
+
332
+ await self._controller.prepare(value)
333
+ self._describe = await self._writer.open(self.name, value.exposures_per_event)
334
+
335
335
  self._initial_frame = await self._writer.get_indices_written()
336
336
  if value.trigger != DetectorTrigger.INTERNAL:
337
337
  await self._controller.arm()
@@ -100,6 +100,11 @@ class Signal(Device, Generic[SignalDatatypeT]):
100
100
  """
101
101
  return self._connector.backend.source(self.name, read=True)
102
102
 
103
+ @property
104
+ def datatype(self) -> type[SignalDatatypeT] | None:
105
+ """Returns the datatype of the signal."""
106
+ return self._connector.backend.datatype
107
+
103
108
 
104
109
  SignalT = TypeVar("SignalT", bound=Signal)
105
110
 
@@ -431,8 +436,8 @@ async def observe_signals_value(
431
436
  Call subscribe_value on all the signals at the start, and clear_sub on
432
437
  it at the end.
433
438
  :param timeout:
434
- If given, how long to wait for each updated value in seconds. If an
435
- update is not produced in this time then raise asyncio.TimeoutError.
439
+ If given, how long to wait for ANY updated value from shared queue in seconds.
440
+ If an update is not produced in this time then raise asyncio.TimeoutError.
436
441
  :param done_status:
437
442
  If this status is complete, stop observing and make the iterator return.
438
443
  If it raises an exception then this exception will be raised by the
@@ -454,8 +459,10 @@ async def observe_signals_value(
454
459
  q: asyncio.Queue[tuple[SignalR[SignalDatatypeT], SignalDatatypeT] | Status] = (
455
460
  asyncio.Queue()
456
461
  )
457
-
462
+ # dict to store signal subscription to remove it later
458
463
  cbs: dict[SignalR, Callback] = {}
464
+
465
+ # subscribe signal to update queue and fill cbs dict
459
466
  for signal in signals:
460
467
 
461
468
  def queue_value(value: SignalDatatypeT, signal=signal):
@@ -468,6 +475,7 @@ async def observe_signals_value(
468
475
  done_status.add_callback(q.put_nowait)
469
476
  overall_deadline = time.monotonic() + done_timeout if done_timeout else None
470
477
  try:
478
+ last_item = ()
471
479
  while True:
472
480
  if overall_deadline and time.monotonic() >= overall_deadline:
473
481
  raise asyncio.TimeoutError(
@@ -476,14 +484,22 @@ async def observe_signals_value(
476
484
  f"timeout {done_timeout}s"
477
485
  )
478
486
  iteration_timeout = _get_iteration_timeout(timeout, overall_deadline)
479
- item = await asyncio.wait_for(q.get(), iteration_timeout)
487
+ try:
488
+ item = await asyncio.wait_for(q.get(), iteration_timeout)
489
+ except asyncio.TimeoutError as exc:
490
+ raise asyncio.TimeoutError(
491
+ f"Timeout Error while waiting {iteration_timeout}s to update "
492
+ f"{[signal.source for signal in signals]}. "
493
+ f"Last observed signal and value were {last_item}"
494
+ ) from exc
480
495
  if done_status and item is done_status:
481
496
  if exc := done_status.exception():
482
497
  raise exc
483
498
  else:
484
499
  break
485
500
  else:
486
- yield cast(tuple[SignalR[SignalDatatypeT], SignalDatatypeT], item)
501
+ last_item = cast(tuple[SignalR[SignalDatatypeT], SignalDatatypeT], item)
502
+ yield last_item
487
503
  finally:
488
504
  for signal, cb in cbs.items():
489
505
  signal.clear_sub(cb)
@@ -6,8 +6,16 @@ import numpy as np
6
6
  from bluesky.protocols import Reading
7
7
  from event_model import DataKey, Dtype, Limits
8
8
 
9
+ from ophyd_async.core._utils import (
10
+ Callback,
11
+ EnumTypes,
12
+ StrictEnum,
13
+ SubsetEnum,
14
+ SupersetEnum,
15
+ get_enum_cls,
16
+ )
17
+
9
18
  from ._table import Table
10
- from ._utils import Callback, StrictEnum, get_enum_cls
11
19
 
12
20
  DTypeScalar_co = TypeVar("DTypeScalar_co", covariant=True, bound=np.generic)
13
21
  """A numpy dtype like [](#numpy.float64)."""
@@ -24,7 +32,7 @@ E.g. `Array1D[np.float64]` is a 1D numpy array of 64-bit floats."""
24
32
  Primitive = bool | int | float | str
25
33
  SignalDatatype = (
26
34
  Primitive
27
- | StrictEnum
35
+ | EnumTypes
28
36
  | Array1D[np.bool_]
29
37
  | Array1D[np.int8]
30
38
  | Array1D[np.uint8]
@@ -39,16 +47,18 @@ SignalDatatype = (
39
47
  | np.ndarray
40
48
  | Sequence[str]
41
49
  | Sequence[StrictEnum]
50
+ | Sequence[SubsetEnum]
51
+ | Sequence[SupersetEnum]
42
52
  | Table
43
53
  )
44
54
  """The supported [](#Signal) datatypes:
45
55
 
46
56
  - A python primitive [](#bool), [](#int), [](#float), [](#str)
47
- - A [](#StrictEnum) or [](#SubsetEnum) subclass
57
+ - An [](#EnumTypes) subclass
48
58
  - A fixed datatype [](#Array1D) of numpy bool, signed and unsigned integers or float
49
59
  - A [](#numpy.ndarray) which can change dimensions and datatype at runtime
50
60
  - A sequence of [](#str)
51
- - A sequence of [](#StrictEnum) or [](#SubsetEnum) subclass
61
+ - A sequence of [](#EnumTypes) subclasses
52
62
  - A [](#Table) subclass
53
63
  """
54
64
  # TODO: These typevars will not be needed when we drop python 3.11
@@ -58,7 +68,7 @@ PrimitiveT = TypeVar("PrimitiveT", bound=Primitive)
58
68
  SignalDatatypeT = TypeVar("SignalDatatypeT", bound=SignalDatatype)
59
69
  """A typevar for a [](#SignalDatatype)."""
60
70
  SignalDatatypeV = TypeVar("SignalDatatypeV", bound=SignalDatatype)
61
- EnumT = TypeVar("EnumT", bound=StrictEnum)
71
+ EnumT = TypeVar("EnumT", bound=EnumTypes)
62
72
  TableT = TypeVar("TableT", bound=Table)
63
73
 
64
74
 
@@ -136,7 +146,7 @@ def _datakey_dtype(datatype: type[SignalDatatype]) -> Dtype:
136
146
  or issubclass(datatype, Table)
137
147
  ):
138
148
  return "array"
139
- elif issubclass(datatype, StrictEnum):
149
+ elif issubclass(datatype, EnumTypes):
140
150
  return "string"
141
151
  elif issubclass(datatype, Primitive):
142
152
  return _primitive_dtype[datatype]
@@ -153,7 +163,7 @@ def _datakey_dtype_numpy(
153
163
  elif (
154
164
  get_origin(datatype) is Sequence
155
165
  or datatype is str
156
- or issubclass(datatype, StrictEnum)
166
+ or issubclass(datatype, EnumTypes)
157
167
  ):
158
168
  # TODO: use np.dtypes.StringDType when we can use in structured arrays
159
169
  # https://github.com/numpy/numpy/issues/25693
@@ -167,7 +177,7 @@ def _datakey_dtype_numpy(
167
177
 
168
178
 
169
179
  def _datakey_shape(value: SignalDatatype) -> list[int]:
170
- if type(value) in _primitive_dtype or isinstance(value, StrictEnum):
180
+ if type(value) in _primitive_dtype or isinstance(value, EnumTypes):
171
181
  return []
172
182
  elif isinstance(value, np.ndarray):
173
183
  return list(value.shape)
@@ -5,7 +5,15 @@ import logging
5
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 Any, Generic, Literal, ParamSpec, TypeVar, get_args, get_origin
8
+ from typing import (
9
+ Any,
10
+ Generic,
11
+ Literal,
12
+ ParamSpec,
13
+ TypeVar,
14
+ get_args,
15
+ get_origin,
16
+ )
9
17
  from unittest.mock import Mock
10
18
 
11
19
  import numpy as np
@@ -19,20 +27,16 @@ DEFAULT_TIMEOUT = 10.0
19
27
  logger = logging.getLogger("ophyd_async")
20
28
 
21
29
 
22
- class StrictEnumMeta(EnumMeta):
23
- def __new__(metacls, *args, **kwargs):
24
- ret = super().__new__(metacls, *args, **kwargs)
30
+ class UppercaseNameEnumMeta(EnumMeta):
31
+ def __new__(cls, *args, **kwargs):
32
+ ret = super().__new__(cls, *args, **kwargs)
25
33
  lowercase_names = [x.name for x in ret if not x.name.isupper()] # type: ignore
26
34
  if lowercase_names:
27
35
  raise TypeError(f"Names {lowercase_names} should be uppercase")
28
36
  return ret
29
37
 
30
38
 
31
- class StrictEnum(str, Enum, metaclass=StrictEnumMeta):
32
- """All members should exist in the Backend, and there will be no extras."""
33
-
34
-
35
- class SubsetEnumMeta(StrictEnumMeta):
39
+ class AnyStringUppercaseNameEnumMeta(UppercaseNameEnumMeta):
36
40
  def __call__(self, value, *args, **kwargs): # type: ignore
37
41
  """Return given value if it is a string and not a member of the enum.
38
42
 
@@ -54,10 +58,21 @@ class SubsetEnumMeta(StrictEnumMeta):
54
58
  return super().__call__(value, *args, **kwargs)
55
59
 
56
60
 
57
- class SubsetEnum(StrictEnum, metaclass=SubsetEnumMeta):
61
+ class StrictEnum(str, Enum, metaclass=UppercaseNameEnumMeta):
62
+ """All members should exist in the Backend, and there will be no extras."""
63
+
64
+
65
+ class SubsetEnum(str, Enum, metaclass=AnyStringUppercaseNameEnumMeta):
58
66
  """All members should exist in the Backend, but there may be extras."""
59
67
 
60
68
 
69
+ class SupersetEnum(str, Enum, metaclass=UppercaseNameEnumMeta):
70
+ """Some members should exist in the Backend, and there should be no extras."""
71
+
72
+
73
+ EnumTypes = StrictEnum | SubsetEnum | SupersetEnum
74
+
75
+
61
76
  CALCULATE_TIMEOUT = "CALCULATE_TIMEOUT"
62
77
  """Sentinel used to implement ``myfunc(timeout=CalculateTimeout)``
63
78
 
@@ -207,10 +222,11 @@ def get_dtype(datatype: type) -> np.dtype:
207
222
  return np.dtype(get_args(get_args(datatype)[1])[0])
208
223
 
209
224
 
210
- def get_enum_cls(datatype: type | None) -> type[StrictEnum] | None:
225
+ def get_enum_cls(datatype: type | None) -> type[EnumTypes] | None:
211
226
  """Get the enum class from a datatype.
212
227
 
213
- :raises TypeError: if type is not a [](#StrictEnum) or [](#SubsetEnum) subclass
228
+ :raises TypeError: if type is not a [](#StrictEnum) or [](#SubsetEnum)
229
+ or [](#SupersetEnum) subclass
214
230
  ```python
215
231
  >>> from ophyd_async.core import StrictEnum
216
232
  >>> from collections.abc import Sequence
@@ -227,10 +243,11 @@ def get_enum_cls(datatype: type | None) -> type[StrictEnum] | None:
227
243
  if get_origin(datatype) is Sequence:
228
244
  datatype = get_args(datatype)[0]
229
245
  if datatype and issubclass(datatype, Enum):
230
- if not issubclass(datatype, StrictEnum):
246
+ if not issubclass(datatype, EnumTypes):
231
247
  raise TypeError(
232
248
  f"{datatype} should inherit from ophyd_async.core.SubsetEnum "
233
- "or ophyd_async.core.StrictEnum"
249
+ "or ophyd_async.core.StrictEnum "
250
+ "or ophyd_async.core.SupersetEnum."
234
251
  )
235
252
  return datatype
236
253
  return None
@@ -22,8 +22,8 @@ class AreaDetector(StandardDetector[ADBaseControllerT, ADWriter]):
22
22
  self.fileio = writer.fileio
23
23
 
24
24
  if plugins is not None:
25
- for name, plugin in plugins.items():
26
- setattr(self, name, plugin)
25
+ for plugin_name, plugin in plugins.items():
26
+ setattr(self, plugin_name, plugin)
27
27
 
28
28
  super().__init__(
29
29
  controller,
@@ -70,14 +70,14 @@ class NDPluginStatsIO(NDPluginBaseIO):
70
70
  # Basic statistics
71
71
  compute_statistics: A[SignalRW[bool], PvSuffix.rbv("ComputeStatistics")]
72
72
  bgd_width: A[SignalRW[int], PvSuffix.rbv("BgdWidth")]
73
- total_array: A[SignalRW[float], PvSuffix.rbv("TotalArray")]
73
+ total: A[SignalR[float], PvSuffix.rbv("Total")]
74
74
  # Centroid statistics
75
75
  compute_centroid: A[SignalRW[bool], PvSuffix.rbv("ComputeCentroid")]
76
76
  centroid_threshold: A[SignalRW[float], PvSuffix.rbv("CentroidThreshold")]
77
77
  # X and Y Profiles
78
78
  compute_profiles: A[SignalRW[bool], PvSuffix.rbv("ComputeProfiles")]
79
- profile_size_x: A[SignalRW[int], PvSuffix.rbv("ProfileSizeX")]
80
- profile_size_y: A[SignalRW[int], PvSuffix.rbv("ProfileSizeY")]
79
+ profile_size_x: A[SignalR[int], PvSuffix.rbv("ProfileSizeX")]
80
+ profile_size_y: A[SignalR[int], PvSuffix.rbv("ProfileSizeY")]
81
81
  cursor_x: A[SignalRW[int], PvSuffix.rbv("CursorX")]
82
82
  cursor_y: A[SignalRW[int], PvSuffix.rbv("CursorY")]
83
83
  # Array Histogram
@@ -7,11 +7,12 @@ from ophyd_async.core import (
7
7
  SignalRW,
8
8
  StrictEnum,
9
9
  SubsetEnum,
10
+ SupersetEnum,
10
11
  wait_for_value,
11
12
  )
12
13
 
13
14
 
14
- class ADBaseDataType(StrictEnum):
15
+ class ADBaseDataType(SupersetEnum):
15
16
  INT8 = "Int8"
16
17
  UINT8 = "UInt8"
17
18
  INT16 = "Int16"
@@ -22,6 +23,9 @@ class ADBaseDataType(StrictEnum):
22
23
  UINT64 = "UInt64"
23
24
  FLOAT32 = "Float32"
24
25
  FLOAT64 = "Float64"
26
+ # Driver database override will blank the enum string if it doesn't
27
+ # support a datatype
28
+ UNDEFINED = ""
25
29
 
26
30
 
27
31
  def convert_ad_dtype_to_np(ad_dtype: ADBaseDataType) -> str:
@@ -37,7 +41,12 @@ def convert_ad_dtype_to_np(ad_dtype: ADBaseDataType) -> str:
37
41
  ADBaseDataType.FLOAT32: "<f4",
38
42
  ADBaseDataType.FLOAT64: "<f8",
39
43
  }
40
- return ad_dtype_to_np_dtype[ad_dtype]
44
+ np_type = ad_dtype_to_np_dtype.get(ad_dtype)
45
+ if np_type is None:
46
+ raise ValueError(
47
+ "Areadetector driver has a blank DataType, this is not supported"
48
+ )
49
+ return np_type
41
50
 
42
51
 
43
52
  def convert_pv_dtype_to_np(datatype: str) -> str:
@@ -27,7 +27,7 @@ class PilatusDetector(AreaDetector[PilatusController]):
27
27
  config_sigs: Sequence[SignalR] = (),
28
28
  ):
29
29
  driver = PilatusDriverIO(prefix + drv_suffix)
30
- controller = PilatusController(driver)
30
+ controller = PilatusController(driver, readout_time=readout_time)
31
31
 
32
32
  writer = writer_cls.with_io(
33
33
  prefix,
@@ -29,6 +29,7 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
29
29
  DetectorTrigger.INTERNAL: PilatusTriggerMode.INTERNAL,
30
30
  DetectorTrigger.CONSTANT_GATE: PilatusTriggerMode.EXT_ENABLE,
31
31
  DetectorTrigger.VARIABLE_GATE: PilatusTriggerMode.EXT_ENABLE,
32
+ DetectorTrigger.EDGE_TRIGGER: PilatusTriggerMode.EXT_TRIGGER,
32
33
  }
33
34
 
34
35
  def __init__(
@@ -49,7 +50,9 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
49
50
  trigger_info.livetime
50
51
  )
51
52
  await asyncio.gather(
52
- self.driver.trigger_mode.set(self._get_trigger_mode(trigger_info.trigger)),
53
+ self.driver.trigger_mode.set(
54
+ self._supported_trigger_types[trigger_info.trigger]
55
+ ),
53
56
  self.driver.num_images.set(
54
57
  999_999
55
58
  if trigger_info.total_number_of_exposures == 0
@@ -70,13 +73,3 @@ class PilatusController(adcore.ADBaseController[PilatusDriverIO]):
70
73
  True,
71
74
  timeout=DEFAULT_TIMEOUT,
72
75
  )
73
-
74
- @classmethod
75
- def _get_trigger_mode(cls, trigger: DetectorTrigger) -> PilatusTriggerMode:
76
- if trigger not in cls._supported_trigger_types.keys():
77
- raise ValueError(
78
- f"{cls.__name__} only supports the following trigger "
79
- f"types: {cls._supported_trigger_types.keys()} but was asked to "
80
- f"use {trigger}"
81
- )
82
- return cls._supported_trigger_types[trigger]
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  import sys
3
3
  import typing
4
- from collections.abc import Sequence
4
+ from collections.abc import Mapping, Sequence
5
5
  from functools import cache
6
6
  from math import isnan, nan
7
7
  from typing import Any, Generic, cast
@@ -146,7 +146,7 @@ class CaBoolConverter(CaConverter[bool]):
146
146
 
147
147
 
148
148
  class CaEnumConverter(CaConverter[str]):
149
- def __init__(self, supported_values: dict[str, str]):
149
+ def __init__(self, supported_values: Mapping[str, str]):
150
150
  self.supported_values = supported_values
151
151
  super().__init__(
152
152
  str, dbr.DBR_STRING, metadata=SignalMetadata(choices=list(supported_values))
@@ -1,16 +1,20 @@
1
- from collections.abc import Sequence
2
- from typing import Any, get_args, get_origin
1
+ from collections.abc import Mapping, Sequence
2
+ from typing import Any, TypeVar, get_args, get_origin
3
3
 
4
4
  import numpy as np
5
5
 
6
6
  from ophyd_async.core import (
7
7
  SignalBackend,
8
8
  SignalDatatypeT,
9
+ StrictEnum,
9
10
  SubsetEnum,
11
+ SupersetEnum,
10
12
  get_dtype,
11
13
  get_enum_cls,
12
14
  )
13
15
 
16
+ T = TypeVar("T")
17
+
14
18
 
15
19
  def get_pv_basename_and_field(pv: str) -> tuple[str, str | None]:
16
20
  """Split PV into record name and field."""
@@ -23,26 +27,30 @@ def get_pv_basename_and_field(pv: str) -> tuple[str, str | None]:
23
27
 
24
28
  def get_supported_values(
25
29
  pv: str,
26
- datatype: type,
30
+ datatype: type[T],
27
31
  pv_choices: Sequence[str],
28
- ) -> dict[str, str]:
32
+ ) -> Mapping[str, T | str]:
29
33
  enum_cls = get_enum_cls(datatype)
30
34
  if not enum_cls:
31
35
  raise TypeError(f"{datatype} is not an Enum")
32
36
  choices = [v.value for v in enum_cls]
33
37
  error_msg = f"{pv} has choices {pv_choices}, but {datatype} requested {choices} "
34
- if issubclass(enum_cls, SubsetEnum):
38
+ if issubclass(enum_cls, StrictEnum):
39
+ if set(choices) != set(pv_choices):
40
+ raise TypeError(error_msg + "to be strictly equal to them.")
41
+ elif issubclass(enum_cls, SubsetEnum):
35
42
  if not set(choices).issubset(pv_choices):
36
43
  raise TypeError(error_msg + "to be a subset of them.")
44
+ elif issubclass(enum_cls, SupersetEnum):
45
+ if not set(pv_choices).issubset(choices):
46
+ raise TypeError(error_msg + "to be a superset of them.")
37
47
  else:
38
- if set(choices) != set(pv_choices):
39
- raise TypeError(error_msg + "to be strictly equal to them.")
40
-
41
- # Take order from the pv choices
42
- supported_values = {x: x for x in pv_choices}
43
- # But override those that we specify via the datatype
44
- for v in enum_cls:
45
- supported_values[v.value] = v
48
+ raise TypeError(f"{datatype} is not a StrictEnum, SubsetEnum, or SupersetEnum")
49
+ # Create a map from the string value to the enum instance
50
+ # For StrictEnum and SupersetEnum, all values here will be enum values
51
+ # For SubsetEnum, only the values in choices will be enum values, the rest will be
52
+ # strings
53
+ supported_values = {x: enum_cls(x) for x in pv_choices}
46
54
  return supported_values
47
55
 
48
56
 
@@ -10,9 +10,10 @@ from ophyd_async.core import (
10
10
  Device,
11
11
  DeviceVector,
12
12
  PathProvider,
13
+ Reference,
14
+ SignalR,
13
15
  StrictEnum,
14
16
  observe_value,
15
- set_and_wait_for_other_value,
16
17
  set_and_wait_for_value,
17
18
  wait_for_value,
18
19
  )
@@ -51,7 +52,7 @@ class Odin(Device):
51
52
 
52
53
  self.capture = epics_signal_rw(Writing, f"{prefix}Capture")
53
54
  self.capture_rbv = epics_signal_r(str, prefix + "Capture_RBV")
54
- self.num_captured = epics_signal_r(int, f"{prefix}NumCapture_RBV")
55
+ self.num_captured = epics_signal_r(int, f"{prefix}NumCaptured_RBV")
55
56
  self.num_to_capture = epics_signal_rw_rbv(int, f"{prefix}NumCapture")
56
57
 
57
58
  self.start_timeout = epics_signal_rw(str, f"{prefix}StartTimeout")
@@ -80,9 +81,11 @@ class OdinWriter(DetectorWriter):
80
81
  self,
81
82
  path_provider: PathProvider,
82
83
  odin_driver: Odin,
84
+ eiger_bit_depth: SignalR[int],
83
85
  ) -> None:
84
86
  self._drv = odin_driver
85
87
  self._path_provider = path_provider
88
+ self._eiger_bit_depth = Reference(eiger_bit_depth)
86
89
  super().__init__()
87
90
 
88
91
  async def open(self, name: str, exposures_per_event: int = 1) -> dict[str, DataKey]:
@@ -92,24 +95,20 @@ class OdinWriter(DetectorWriter):
92
95
  await asyncio.gather(
93
96
  self._drv.file_path.set(str(info.directory_path)),
94
97
  self._drv.file_name.set(info.filename),
95
- self._drv.data_type.set(
96
- "UInt16"
97
- ), # TODO: Get from eiger https://github.com/bluesky/ophyd-async/issues/529
98
+ self._drv.data_type.set(f"UInt{await self._eiger_bit_depth().get_value()}"),
98
99
  self._drv.num_to_capture.set(0),
99
100
  )
100
101
 
101
102
  await wait_for_value(self._drv.meta_active, "Active", timeout=DEFAULT_TIMEOUT)
102
103
 
103
- await set_and_wait_for_other_value(
104
- self._drv.capture,
105
- Writing.CAPTURE,
106
- self._drv.capture_rbv,
107
- "Capturing",
108
- set_timeout=None,
109
- wait_for_set_completion=False,
104
+ await self._drv.capture.set(
105
+ Writing.CAPTURE, wait=False
110
106
  ) # TODO: Investigate why we do not get a put callback when setting capture pv https://github.com/bluesky/ophyd-async/issues/866
111
107
 
112
- await wait_for_value(self._drv.meta_writing, "Writing", timeout=DEFAULT_TIMEOUT)
108
+ await asyncio.gather(
109
+ wait_for_value(self._drv.capture_rbv, "Capturing", timeout=DEFAULT_TIMEOUT),
110
+ wait_for_value(self._drv.meta_writing, "Writing", timeout=DEFAULT_TIMEOUT),
111
+ )
113
112
 
114
113
  return await self._describe()
115
114
 
@@ -260,10 +260,11 @@ class Motor(
260
260
  (await self.acceleration_time.get_value()) * fly_velocity * 0.5
261
261
  )
262
262
 
263
- self._fly_completed_position = end_position + run_up_distance
263
+ direction = 1 if end_position > start_position else -1
264
+ self._fly_completed_position = end_position + (run_up_distance * direction)
264
265
 
265
266
  # Prepared position not used after prepare, so no need to store in self
266
- fly_prepared_position = start_position - run_up_distance
267
+ fly_prepared_position = start_position - (run_up_distance * direction)
267
268
 
268
269
  motor_lower_limit, motor_upper_limit, egu = await asyncio.gather(
269
270
  self.low_limit_travel.get_value(),