ophyd-async 0.14.1__tar.gz → 0.14.2__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 (329) hide show
  1. {ophyd_async-0.14.1/src/ophyd_async.egg-info → ophyd_async-0.14.2}/PKG-INFO +1 -1
  2. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/_version.py +3 -3
  3. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_derived_signal.py +56 -23
  4. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_device_filler.py +28 -3
  5. {ophyd_async-0.14.1 → ophyd_async-0.14.2/src/ophyd_async.egg-info}/PKG-INFO +1 -1
  6. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_device.py +48 -0
  7. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_multi_derived_signal.py +62 -0
  8. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_observe.py +8 -4
  9. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_single_derived_signal.py +24 -1
  10. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/pvi/test_pvi.py +17 -0
  11. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.codecov.yml +0 -0
  12. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.copier-answers.yml +0 -0
  13. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.devcontainer/devcontainer.json +0 -0
  14. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.git-blame-ignore-revs +0 -0
  15. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/CONTRIBUTING.md +0 -0
  16. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  17. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  18. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  19. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/codeql/codeql-config.yml +0 -0
  20. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/pages/index.html +0 -0
  21. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/pages/make_switcher.py +0 -0
  22. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_codeql.yml +0 -0
  23. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_dist.yml +0 -0
  24. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_docs.yml +0 -0
  25. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_pypi.yml +0 -0
  26. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_release.yml +0 -0
  27. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_test.yml +0 -0
  28. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/_tox.yml +0 -0
  29. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/ci.yml +0 -0
  30. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/claude.yml +0 -0
  31. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.github/workflows/periodic.yml +0 -0
  32. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.gitignore +0 -0
  33. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.gitleaks.toml +0 -0
  34. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.gitmodules +0 -0
  35. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.pre-commit-config.yaml +0 -0
  36. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/.python-version +0 -0
  37. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/Dockerfile +0 -0
  38. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/LICENSE +0 -0
  39. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/README.md +0 -0
  40. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/_static/custom.css +0 -0
  41. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/conf.py +0 -0
  42. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
  43. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
  44. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
  45. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
  46. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
  47. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
  48. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0007-subpackage-structure.md +0 -0
  49. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0008-signal-types.md +0 -0
  50. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0009-procedural-vs-declarative-devices.md +0 -0
  51. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0010-docstring-format.md +0 -0
  52. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/0011-buffer-updates-camonitor.md +0 -0
  53. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions/COPYME +0 -0
  54. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/decisions.md +0 -0
  55. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/declarative-vs-procedural.md +0 -0
  56. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/design-goals.md +0 -0
  57. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/device-connection-strategies.md +0 -0
  58. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/devices-signals-backends.md +0 -0
  59. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/fly-scanning.md +0 -0
  60. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/plan-stubs.md +0 -0
  61. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/when-to-extend-movable.md +0 -0
  62. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations/where-device-logic.md +0 -0
  63. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/explanations.md +0 -0
  64. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/genindex.rst +0 -0
  65. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/choose-right-baseclass.md +0 -0
  66. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/contribute.md +0 -0
  67. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/derive-one-signal-from-others.md +0 -0
  68. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/implement-ad-detector.md +0 -0
  69. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/interact-with-signals.md +0 -0
  70. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/put-device-back.md +0 -0
  71. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to/store-and-retrieve.md +0 -0
  72. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/how-to.md +0 -0
  73. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/images/fly_scan_collection_windows_and_frames.svg +0 -0
  74. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/images/ophyd-async-logo.svg +0 -0
  75. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/images/ophyd-favicon.svg +0 -0
  76. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/images/set_and_wait_for_other_value.excalidraw.svg +0 -0
  77. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/index.md +0 -0
  78. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/reference.md +0 -0
  79. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/tutorials/implementing-detectors.md +0 -0
  80. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/tutorials/implementing-devices.md +0 -0
  81. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/tutorials/installation.md +0 -0
  82. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/tutorials/using-devices.md +0 -0
  83. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/tutorials/writing-tests-for-devices.md +0 -0
  84. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/docs/tutorials.md +0 -0
  85. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/pyproject.toml +0 -0
  86. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/renovate.json +0 -0
  87. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/setup.cfg +0 -0
  88. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/__init__.py +0 -0
  89. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/__main__.py +0 -0
  90. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/_docs_parser.py +0 -0
  91. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/__init__.py +0 -0
  92. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_derived_signal_backend.py +0 -0
  93. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_detector.py +0 -0
  94. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_device.py +0 -0
  95. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_enums.py +0 -0
  96. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_flyer.py +0 -0
  97. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_hdf_dataset.py +0 -0
  98. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_log.py +0 -0
  99. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_mock_signal_backend.py +0 -0
  100. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_mock_signal_utils.py +0 -0
  101. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_protocol.py +0 -0
  102. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_providers.py +0 -0
  103. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_readable.py +0 -0
  104. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_settings.py +0 -0
  105. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_signal.py +0 -0
  106. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_signal_backend.py +0 -0
  107. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_soft_signal_backend.py +0 -0
  108. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_status.py +0 -0
  109. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_table.py +0 -0
  110. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_utils.py +0 -0
  111. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/core/_yaml_settings.py +0 -0
  112. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/__init__.py +0 -0
  113. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adandor/__init__.py +0 -0
  114. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adandor/_andor.py +0 -0
  115. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adandor/_andor_controller.py +0 -0
  116. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adandor/_andor_io.py +0 -0
  117. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adaravis/__init__.py +0 -0
  118. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adaravis/_aravis.py +0 -0
  119. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adaravis/_aravis_controller.py +0 -0
  120. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adaravis/_aravis_io.py +0 -0
  121. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/__init__.py +0 -0
  122. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_core_detector.py +0 -0
  123. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_core_io.py +0 -0
  124. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_core_logic.py +0 -0
  125. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_core_writer.py +0 -0
  126. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_hdf_writer.py +0 -0
  127. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_jpeg_writer.py +0 -0
  128. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_single_trigger.py +0 -0
  129. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_tiff_writer.py +0 -0
  130. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adcore/_utils.py +0 -0
  131. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adkinetix/__init__.py +0 -0
  132. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adkinetix/_kinetix.py +0 -0
  133. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adkinetix/_kinetix_controller.py +0 -0
  134. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adkinetix/_kinetix_io.py +0 -0
  135. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adpilatus/__init__.py +0 -0
  136. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adpilatus/_pilatus.py +0 -0
  137. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adpilatus/_pilatus_controller.py +0 -0
  138. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adpilatus/_pilatus_io.py +0 -0
  139. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adsimdetector/__init__.py +0 -0
  140. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adsimdetector/_sim.py +0 -0
  141. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adsimdetector/_sim_controller.py +0 -0
  142. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/adsimdetector/_sim_io.py +0 -0
  143. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/advimba/__init__.py +0 -0
  144. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/advimba/_vimba.py +0 -0
  145. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/advimba/_vimba_controller.py +0 -0
  146. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/advimba/_vimba_io.py +0 -0
  147. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/__init__.py +0 -0
  148. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_aioca.py +0 -0
  149. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_epics_connector.py +0 -0
  150. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_epics_device.py +0 -0
  151. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_p4p.py +0 -0
  152. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_pvi_connector.py +0 -0
  153. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_signal.py +0 -0
  154. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/core/_util.py +0 -0
  155. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/__init__.py +0 -0
  156. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/__main__.py +0 -0
  157. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/_ioc.py +0 -0
  158. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/_motor.py +0 -0
  159. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/_point_detector.py +0 -0
  160. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/_point_detector_channel.py +0 -0
  161. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/_stage.py +0 -0
  162. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/motor.db +0 -0
  163. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/point_detector.db +0 -0
  164. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/demo/point_detector_channel.db +0 -0
  165. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/motor.py +0 -0
  166. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/odin/__init__.py +0 -0
  167. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/odin/_odin_io.py +0 -0
  168. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/pmac/__init__.py +0 -0
  169. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/pmac/_pmac_io.py +0 -0
  170. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/pmac/_pmac_trajectory.py +0 -0
  171. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/pmac/_pmac_trajectory_generation.py +0 -0
  172. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/pmac/_utils.py +0 -0
  173. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/signal.py +0 -0
  174. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/testing/__init__.py +0 -0
  175. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/testing/_example_ioc.py +0 -0
  176. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/testing/_utils.py +0 -0
  177. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/testing/test_records.db +0 -0
  178. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/epics/testing/test_records_pva.db +0 -0
  179. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/__init__.py +0 -0
  180. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/core.py +0 -0
  181. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/eiger/__init__.py +0 -0
  182. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/eiger/_eiger.py +0 -0
  183. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/eiger/_eiger_controller.py +0 -0
  184. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/eiger/_eiger_io.py +0 -0
  185. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/jungfrau/__init__.py +0 -0
  186. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/jungfrau/_controller.py +0 -0
  187. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/jungfrau/_jungfrau.py +0 -0
  188. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/jungfrau/_signals.py +0 -0
  189. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/jungfrau/_utils.py +0 -0
  190. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/odin/__init__.py +0 -0
  191. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/__init__.py +0 -0
  192. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/_block.py +0 -0
  193. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/_control.py +0 -0
  194. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/_hdf_panda.py +0 -0
  195. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/_table.py +0 -0
  196. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/_trigger.py +0 -0
  197. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/fastcs/panda/_writer.py +0 -0
  198. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/__init__.py +0 -0
  199. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_ensure_connected.py +0 -0
  200. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_fly.py +0 -0
  201. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_nd_attributes.py +0 -0
  202. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_panda.py +0 -0
  203. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_settings.py +0 -0
  204. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_utils.py +0 -0
  205. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/plan_stubs/_wait_for_awaitable.py +0 -0
  206. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/py.typed +0 -0
  207. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/__init__.py +0 -0
  208. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/__main__.py +0 -0
  209. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_blob_detector.py +0 -0
  210. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_blob_detector_controller.py +0 -0
  211. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_blob_detector_writer.py +0 -0
  212. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_mirror_horizontal.py +0 -0
  213. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_mirror_vertical.py +0 -0
  214. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_motor.py +0 -0
  215. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_pattern_generator.py +0 -0
  216. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_point_detector.py +0 -0
  217. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/sim/_stage.py +0 -0
  218. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/__init__.py +0 -0
  219. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/core/__init__.py +0 -0
  220. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/core/_base_device.py +0 -0
  221. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/core/_converters.py +0 -0
  222. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/core/_signal.py +0 -0
  223. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/core/_tango_transport.py +0 -0
  224. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/core/_utils.py +0 -0
  225. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/demo/__init__.py +0 -0
  226. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/demo/_counter.py +0 -0
  227. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/demo/_detector.py +0 -0
  228. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/demo/_mover.py +0 -0
  229. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/demo/_tango/__init__.py +0 -0
  230. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/demo/_tango/_servers.py +0 -0
  231. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/testing/__init__.py +0 -0
  232. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/testing/_one_of_everything.py +0 -0
  233. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/tango/testing/_test_config.py +0 -0
  234. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/__init__.py +0 -0
  235. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/__pytest_assert_rewrite.py +0 -0
  236. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/_assert.py +0 -0
  237. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/_one_of_everything.py +0 -0
  238. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/_single_derived.py +0 -0
  239. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/_utils.py +0 -0
  240. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async/testing/_wait_for_pending.py +0 -0
  241. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async.egg-info/SOURCES.txt +0 -0
  242. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
  243. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async.egg-info/requires.txt +0 -0
  244. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/src/ophyd_async.egg-info/top_level.txt +0 -0
  245. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/README.md +0 -0
  246. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/conftest.py +0 -0
  247. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/__init__.py +0 -0
  248. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/conftest.py +0 -0
  249. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/adsim/baseline.yaml +0 -0
  250. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/adsim/external_dependencies.sh +0 -0
  251. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/adsim/test_adsim_system.py +0 -0
  252. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/eiger/README.md +0 -0
  253. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/eiger/start_iocs_and_run_tests.sh +0 -0
  254. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/eiger/test_eiger_system.py +0 -0
  255. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/signal/test_signals.py +0 -0
  256. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/signal/test_yaml_save_ca.yaml +0 -0
  257. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/epics/signal/test_yaml_save_pva.yaml +0 -0
  258. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/fastcs/panda/test_panda_connect.py +0 -0
  259. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests/test_tutorials.py +0 -0
  260. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests_tango/conftest.py +0 -0
  261. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests_tango/context_subprocess.py +0 -0
  262. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests_tango/test_base_device.py +0 -0
  263. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests_tango/test_tango_signals.py +0 -0
  264. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/system_tests_tango/test_tango_transport.py +0 -0
  265. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/__init__.py +0 -0
  266. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_auto_init_devices.py +0 -0
  267. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_detector.py +0 -0
  268. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_flyer.py +0 -0
  269. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_log.py +0 -0
  270. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_mock_signal_backend.py +0 -0
  271. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_protocol.py +0 -0
  272. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_providers.py +0 -0
  273. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_readable.py +0 -0
  274. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_signal.py +0 -0
  275. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_soft_signal_backend.py +0 -0
  276. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_status.py +0 -0
  277. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_subset_enum.py +0 -0
  278. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_table.py +0 -0
  279. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_utils.py +0 -0
  280. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/core/test_watchable_async_status.py +0 -0
  281. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adandor/test_andor.py +0 -0
  282. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adaravis/test_aravis.py +0 -0
  283. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_cont_acq_detector.py +0 -0
  284. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_detectors.py +0 -0
  285. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_drivers.py +0 -0
  286. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_plugins.py +0 -0
  287. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_scans.py +0 -0
  288. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_single_trigger.py +0 -0
  289. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adcore/test_writers.py +0 -0
  290. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adkinetix/test_kinetix.py +0 -0
  291. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adpilatus/test_pilatus.py +0 -0
  292. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/adsimdetector/test_sim.py +0 -0
  293. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/advimba/test_vimba.py +0 -0
  294. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/conftest.py +0 -0
  295. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/demo/test_epics_demo.py +0 -0
  296. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/eiger/test_odin_io.py +0 -0
  297. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/pmac/conftest.py +0 -0
  298. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/pmac/test_pmac_io.py +0 -0
  299. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/pmac/test_pmac_trajectory.py +0 -0
  300. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/pmac/test_pmac_trajectory_generation.py +0 -0
  301. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/pmac/test_pmac_utils.py +0 -0
  302. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/signal/test_common.py +0 -0
  303. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/test_areadetector_subclass_naming.py +0 -0
  304. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/epics/test_motor.py +0 -0
  305. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/eiger/test_eiger_controller.py +0 -0
  306. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/eiger/test_eiger_detector.py +0 -0
  307. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/jungfrau/__init__.py +0 -0
  308. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/jungfrau/test_controller.py +0 -0
  309. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/jungfrau/test_utils.py +0 -0
  310. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/db/panda.db +0 -0
  311. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_hdf_panda.py +0 -0
  312. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_panda_connect_mock.py +0 -0
  313. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_panda_control.py +0 -0
  314. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_panda_utils.py +0 -0
  315. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_seq_table.py +0 -0
  316. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_trigger.py +0 -0
  317. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/fastcs/panda/test_writer.py +0 -0
  318. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/plan_stubs/test_ensure_connected.py +0 -0
  319. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/plan_stubs/test_fly.py +0 -0
  320. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/plan_stubs/test_settings.py +0 -0
  321. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/plan_stubs/test_setup.py +0 -0
  322. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/sim/__init__.py +0 -0
  323. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/sim/test_sim_blob_detector.py +0 -0
  324. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/sim/test_sim_motor.py +0 -0
  325. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/test_branching.py +0 -0
  326. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/test_cli.py +0 -0
  327. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/test_data/test_yaml_config_save.yaml +0 -0
  328. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/tests/unit_tests/test_data/test_yaml_save.yaml +0 -0
  329. {ophyd_async-0.14.1 → ophyd_async-0.14.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ophyd-async
3
- Version: 0.14.1
3
+ Version: 0.14.2
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
@@ -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.14.2'
32
+ __version_tuple__ = version_tuple = (0, 14, 2)
33
33
 
34
- __commit_id__ = commit_id = 'g9b567b214'
34
+ __commit_id__ = commit_id = 'ga221add53'
@@ -1,5 +1,15 @@
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
+ get_type_hints,
11
+ is_typeddict,
12
+ )
3
13
 
4
14
  from bluesky.protocols import Locatable
5
15
 
@@ -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
  )
@@ -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
@@ -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,8 +10,10 @@ from typing import (
9
10
  NoReturn,
10
11
  Protocol,
11
12
  TypeVar,
13
+ Union,
12
14
  cast,
13
15
  get_args,
16
+ get_origin,
14
17
  get_type_hints,
15
18
  runtime_checkable,
16
19
  )
@@ -76,6 +79,7 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
76
79
  self._extras: dict[UniqueName, Sequence[Any]] = {}
77
80
  self._signal_datatype: dict[LogicalName, type | None] = {}
78
81
  self._vector_device_type: dict[LogicalName, type[Device] | None] = {}
82
+ self._optional_devices: set[str] = set()
79
83
  self.ignored_signals: set[str] = set()
80
84
  # Backends and Connectors stored ready for the connection phase
81
85
  self._unfilled_backends: dict[
@@ -121,6 +125,20 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
121
125
  self.ignored_signals.add(attr_name)
122
126
  name = UniqueName(attr_name)
123
127
  origin = get_origin_class(annotation)
128
+ args = get_args(annotation)
129
+
130
+ if (
131
+ get_origin(annotation) is Union
132
+ and types.NoneType in args
133
+ and len(args) == 2
134
+ ):
135
+ # Annotation is an Union with two arguments, one of which is None
136
+ # Make this signal an optional parameter and set origin to T
137
+ # so the device is added to unfilled_connectors
138
+ self._optional_devices.add(name)
139
+ (annotation,) = [x for x in args if x is not types.NoneType]
140
+ origin = get_origin_class(annotation)
141
+
124
142
  if (
125
143
  name == "parent"
126
144
  or name.startswith("_")
@@ -241,10 +259,17 @@ class DeviceFiller(Generic[SignalBackendT, DeviceConnectorT]):
241
259
  :param source: The source of the data that should have done the filling, for
242
260
  reporting as an error message
243
261
  """
244
- unfilled = sorted(set(self._unfilled_connectors).union(self._unfilled_backends))
245
- if unfilled:
262
+ unfilled = set(self._unfilled_connectors).union(self._unfilled_backends)
263
+ unfilled_optional = sorted(unfilled.intersection(self._optional_devices))
264
+
265
+ for name in unfilled_optional:
266
+ setattr(self._device, name, None)
267
+
268
+ required = sorted(unfilled.difference(unfilled_optional))
269
+
270
+ if required:
246
271
  raise RuntimeError(
247
- f"{self._device.name}: cannot provision {unfilled} from {source}"
272
+ f"{self._device.name}: cannot provision {required} from {source}"
248
273
  )
249
274
 
250
275
  def _ensure_device_vector(self, name: LogicalName) -> DeviceVector:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ophyd-async
3
- Version: 0.14.1
3
+ Version: 0.14.2
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
@@ -8,6 +8,7 @@ import pytest
8
8
  from ophyd_async.core import (
9
9
  DEFAULT_TIMEOUT,
10
10
  Device,
11
+ DeviceFiller,
11
12
  DeviceVector,
12
13
  NotConnectedError,
13
14
  Reference,
@@ -266,3 +267,50 @@ def test_setitem_with_non_device_value():
266
267
  device_vector = DeviceVector(children={})
267
268
  with pytest.raises(TypeError, match="Expected Device, got"):
268
269
  device_vector[1] = "not_a_device"
270
+
271
+
272
+ def test_device_filler_check_filled_with_optional_signals():
273
+ """Test DeviceFiller.check_filled with both mandatory and optional Signals."""
274
+
275
+ class TestDevice(Device):
276
+ mandatory_signal: SignalRW[int]
277
+ optional_signal: SignalRW[int] | None
278
+
279
+ # Create a mock backend factory
280
+ def mock_backend_factory(datatype):
281
+ backend = Mock()
282
+ backend.datatype = datatype
283
+ return backend
284
+
285
+ # Create a mock connector factory
286
+ def mock_connector_factory():
287
+ return Mock()
288
+
289
+ device = TestDevice()
290
+ filler = DeviceFiller(
291
+ device=device,
292
+ signal_backend_factory=mock_backend_factory,
293
+ device_connector_factory=mock_connector_factory,
294
+ )
295
+
296
+ # Create signals from annotations (unfilled)
297
+ list(filler.create_signals_from_annotations(filled=False))
298
+
299
+ assert hasattr(device, "optional_signal")
300
+ assert isinstance(device.optional_signal, SignalRW)
301
+
302
+ # Test failure path: check_filled should fail when mandatory signal is unfilled
303
+ with pytest.raises(RuntimeError, match="cannot provision.*mandatory_signal"):
304
+ filler.check_filled("test_source")
305
+
306
+ # Fill the mandatory signal
307
+ filler.fill_child_signal("mandatory_signal", SignalRW, None)
308
+
309
+ # Test success path: check_filled should succeed and set optional_signal to None
310
+ filler.check_filled("test_source")
311
+
312
+ # Verify mandatory signal exists and optional signal is None
313
+ assert hasattr(device, "mandatory_signal")
314
+ assert isinstance(device.mandatory_signal, SignalRW)
315
+ assert hasattr(device, "optional_signal")
316
+ assert device.optional_signal is None
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import math
3
3
  import re
4
+ from typing import TypeVar
4
5
  from unittest.mock import ANY, call
5
6
 
6
7
  import pytest
@@ -8,8 +9,12 @@ from bluesky.protocols import Reading
8
9
 
9
10
  from ophyd_async.core import (
10
11
  DerivedSignalFactory,
12
+ EnableDisable,
13
+ EnumTypes,
11
14
  SignalRW,
15
+ StrictEnum,
12
16
  Table,
17
+ Transform,
13
18
  derived_signal_rw,
14
19
  get_mock,
15
20
  set_mock_value,
@@ -187,3 +192,60 @@ def test_make_rw_signal_type_mismatch():
187
192
  match=re.escape("Must define a set_derived method to support derived"),
188
193
  ):
189
194
  factory.derived_signal_rw(datatype=Table, name="")
195
+
196
+
197
+ def test_missing_type_hint_in_raw_to_derived_transform():
198
+ class UnTypedTransform(Transform):
199
+ def raw_to_derived(self, x) -> float:
200
+ return x
201
+
202
+ with pytest.raises(
203
+ TypeError,
204
+ match=re.escape(" is missing a type hint for arguments: ['x']"),
205
+ ):
206
+ DerivedSignalFactory(
207
+ UnTypedTransform,
208
+ set_derived=None,
209
+ x=soft_signal_rw(float),
210
+ )
211
+
212
+
213
+ def test_sub_type_hint_in_raw_to_derived_transform():
214
+ class SubTypedTransform(Transform):
215
+ def raw_to_derived(self, x: StrictEnum) -> StrictEnum:
216
+ return x
217
+
218
+ DerivedSignalFactory(
219
+ SubTypedTransform,
220
+ set_derived=None,
221
+ x=soft_signal_rw(EnableDisable),
222
+ )
223
+
224
+ with pytest.raises(
225
+ TypeError,
226
+ match=re.escape(
227
+ "Expected the following to be passed as keyword arguments "
228
+ "{'x': <enum 'StrictEnum'>}, "
229
+ "got {'x': <class 'float'>}"
230
+ ),
231
+ ):
232
+ DerivedSignalFactory(
233
+ SubTypedTransform,
234
+ set_derived=None,
235
+ x=soft_signal_rw(float),
236
+ )
237
+
238
+
239
+ EnumTypesT = TypeVar("EnumTypesT", bound=EnumTypes)
240
+
241
+
242
+ def test_protocol_type_hint_in_raw_to_derived_transform():
243
+ class SubTypedTransform(Transform):
244
+ def raw_to_derived(self, x: EnumTypesT) -> EnumTypesT:
245
+ return x
246
+
247
+ DerivedSignalFactory(
248
+ SubTypedTransform,
249
+ set_derived=None,
250
+ x=soft_signal_rw(EnableDisable),
251
+ )
@@ -113,17 +113,21 @@ async def test_observe_value_times_out_with_no_external_task():
113
113
 
114
114
  recv = []
115
115
 
116
+ start = time.monotonic()
117
+
116
118
  async def watch(done_timeout):
117
119
  async for val in observe_value(sig, done_timeout=done_timeout):
118
- recv.append(val)
120
+ recv.append(time.monotonic() - start)
119
121
  setter(val + 1)
120
122
 
121
- start = time.monotonic()
122
123
  with pytest.raises(asyncio.TimeoutError):
123
- await watch(done_timeout=0.1)
124
+ await watch(done_timeout=0.07)
124
125
  # On a dev machine we can do >200 iterations in 0.1s, but CI is slower
125
126
  assert len(recv) > 10
126
- assert time.monotonic() - start == pytest.approx(0.1, abs=0.05)
127
+ elapsed = time.monotonic() - start
128
+ assert elapsed == pytest.approx(0.07, abs=0.05), (
129
+ f"Elapsed: {elapsed} Received: {recv}"
130
+ )
127
131
 
128
132
 
129
133
  async def test_observe_value_uses_correct_timeout():
@@ -88,7 +88,7 @@ async def test_get_returns_right_position(
88
88
 
89
89
  @pytest.mark.parametrize("cls", [ReadOnlyBeamstop, MovableBeamstop])
90
90
  async def test_monitoring_position(cls: type[ReadOnlyBeamstop | MovableBeamstop]):
91
- results = asyncio.Queue[BeamstopPosition]()
91
+ results: asyncio.Queue[dict[str, Reading]] = asyncio.Queue()
92
92
  inst = cls("inst")
93
93
  inst.position.subscribe_reading(results.put_nowait)
94
94
  assert (await results.get())["inst-position"][
@@ -181,6 +181,29 @@ async def _put(value: float) -> None:
181
181
  pass
182
182
 
183
183
 
184
+ # function without type hint on first argument
185
+ def _get_no_type(ts) -> float:
186
+ return ts
187
+
188
+
189
+ async def test_derived_signal_rw_get_method_no_param_type():
190
+ signal_rw = soft_signal_rw(int, initial_value=4)
191
+ with pytest.raises(
192
+ TypeError,
193
+ match=re.escape(" is missing a type hint for arguments: ['ts']"),
194
+ ):
195
+ derived_signal_rw(_get_no_type, _put, ts=signal_rw)
196
+
197
+
198
+ async def test_derived_signal_r_get_method_no_param_type():
199
+ signal_rw = soft_signal_rw(int, initial_value=4)
200
+ with pytest.raises(
201
+ TypeError,
202
+ match=re.escape(" is missing a type hint for arguments: ['ts']"),
203
+ ):
204
+ derived_signal_r(_get_no_type, ts=signal_rw)
205
+
206
+
184
207
  @pytest.fixture
185
208
  def derived_signal_backend() -> SignalBackend[SignalDatatype]:
186
209
  signal_rw = soft_signal_rw(int, initial_value=4)
@@ -52,6 +52,10 @@ class Block4(StandardReadable):
52
52
  signal_rw: SignalRW[int]
53
53
 
54
54
 
55
+ class Block5(Device):
56
+ signal_rw: SignalRW[int] | None
57
+
58
+
55
59
  DeviceT = TypeVar("DeviceT", bound=Device)
56
60
 
57
61
 
@@ -133,6 +137,19 @@ async def test_device_create_children_from_annotations():
133
137
  assert device.signal_device is top_block_1_device
134
138
 
135
139
 
140
+ async def test_device_create_device_with_optional_signals():
141
+ device = with_pvi_connector(Block5, "PREFIX:")
142
+
143
+ # Makes sure before connecting we create a signal and it exists in the device
144
+ assert hasattr(device, "signal_rw")
145
+ assert isinstance(device.signal_rw, SignalRW)
146
+
147
+ await device.connect(mock=True)
148
+
149
+ # After connecting if the optional signal is not filled it's set to None
150
+ assert isinstance(device.signal_rw, SignalRW)
151
+
152
+
136
153
  async def test_device_create_children_from_annotations_with_device_vectors():
137
154
  device = with_pvi_connector(Block4, "PREFIX:", name="test_device")
138
155
  await device.connect(mock=True)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes