pycontrol-core 3.0.0b2__tar.gz → 3.0.0b3__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 (311) hide show
  1. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/PKG-INFO +1 -1
  2. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/changelog.md +7 -0
  3. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/public-api.md +10 -5
  4. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/task-handlers.md +10 -0
  5. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/__init__.py +1 -1
  6. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/addons/__init__.py +5 -1
  7. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bus/__init__.py +3 -1
  8. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bus/status.py +50 -2
  9. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/framework.py +1 -1
  10. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_status_fields.py +15 -1
  11. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.complexipy_cache/.gitignore +0 -0
  12. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.complexipy_cache/CACHEDIR.TAG +0 -0
  13. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.complexipy_cache/README.md +0 -0
  14. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.complexipy_cache/v/cache/functions +0 -0
  15. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.gitignore +0 -0
  16. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.pre-commit-config.yaml +0 -0
  17. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/.python-version +0 -0
  18. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/CONTRIBUTING.md +0 -0
  19. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/LICENSE.txt +0 -0
  20. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/README.md +0 -0
  21. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/addon-settings.md +0 -0
  22. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/architecture.md +0 -0
  23. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/cli-reference.md +0 -0
  24. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/contracts.md +0 -0
  25. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/core-walkthrough.md +0 -0
  26. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/data-format.md +0 -0
  27. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/design-history/simplified-pycontrol-provenance-and-sharing-motivation.md +0 -0
  28. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/design-history/simplified-pycontrol-provenance-and-sharing-plan.md +0 -0
  29. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/experiment-handlers.md +0 -0
  30. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/getting-started.md +0 -0
  31. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/hardware-definitions.md +0 -0
  32. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/maintainability.md +0 -0
  33. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/migration-from-official.md +0 -0
  34. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/release-acceptance.md +0 -0
  35. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/sharing.md +0 -0
  36. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/subscriber-api.md +0 -0
  37. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/testing.md +0 -0
  38. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/threading-and-state.md +0 -0
  39. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/troubleshooting.md +0 -0
  40. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/wire-protocol.md +0 -0
  41. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/docs/writing-tasks.md +0 -0
  42. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/pyproject.toml +0 -0
  43. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/addons/README.md +0 -0
  44. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/addons/controls_context.py +0 -0
  45. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/addons/experiment_api.py +0 -0
  46. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/addons/loader.py +0 -0
  47. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/addons/task_api.py +0 -0
  48. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/analysis/README.md +0 -0
  49. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/analysis/__init__.py +0 -0
  50. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/analysis/data_import.py +0 -0
  51. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/README.md +0 -0
  52. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/__init__.py +0 -0
  53. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/collect.py +0 -0
  54. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/models.py +0 -0
  55. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/protocol.py +0 -0
  56. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/run.py +0 -0
  57. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bundles/zipio.py +0 -0
  58. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bus/README.md +0 -0
  59. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bus/events.py +0 -0
  60. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/bus/subscriber.py +0 -0
  61. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/README.md +0 -0
  62. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/__init__.py +0 -0
  63. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/__init__.py +0 -0
  64. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/board.py +0 -0
  65. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/board_impl.py +0 -0
  66. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/common.py +0 -0
  67. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/data.py +0 -0
  68. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/data_impl.py +0 -0
  69. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/experiment.py +0 -0
  70. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/experiment_impl.py +0 -0
  71. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/firmware.py +0 -0
  72. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/firmware_impl.py +0 -0
  73. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/gui.py +0 -0
  74. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/gui_impl.py +0 -0
  75. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/run.py +0 -0
  76. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/run_impl.py +0 -0
  77. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/setups.py +0 -0
  78. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/setups_impl.py +0 -0
  79. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/vars.py +0 -0
  80. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/vars_impl.py +0 -0
  81. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/workspace.py +0 -0
  82. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/commands/workspace_impl.py +0 -0
  83. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/cli/main.py +0 -0
  84. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/config/README.md +0 -0
  85. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/config/__init__.py +0 -0
  86. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/config/io.py +0 -0
  87. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/config/models.py +0 -0
  88. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/README.md +0 -0
  89. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/__init__.py +0 -0
  90. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/hooks.py +0 -0
  91. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/models.py +0 -0
  92. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/planning.py +0 -0
  93. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/run.py +0 -0
  94. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/run_recording.py +0 -0
  95. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/experiment/supervisor.py +0 -0
  96. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/README.md +0 -0
  97. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/__init__.py +0 -0
  98. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/device_helpers.py +0 -0
  99. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/LED_driver.py +0 -0
  100. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/MCP.py +0 -0
  101. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/VL53L4.py +0 -0
  102. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/__init__.py +0 -0
  103. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/analog_LED.py +0 -0
  104. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/audio_board.py +0 -0
  105. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/audio_player.py +0 -0
  106. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/breakout_10port_r0_4_h7.py +0 -0
  107. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/breakout_10port_r0_8_h7.py +0 -0
  108. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/breakout_6port_r0_1_h7.py +0 -0
  109. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/breakout_6port_r0_2_h7.py +0 -0
  110. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/breakout_6port_r1_2_f4.py +0 -0
  111. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/five_poke.py +0 -0
  112. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/frame_logger.py +0 -0
  113. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/frame_trigger.py +0 -0
  114. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/grid_maze.py +0 -0
  115. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/lickometer.py +0 -0
  116. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/load_cell.py +0 -0
  117. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/nine_poke.py +0 -0
  118. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/poke.py +0 -0
  119. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/port_expander.py +0 -0
  120. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/rotary_encoder.py +0 -0
  121. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/schmitt_trigger.py +0 -0
  122. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/solenoid_driver.py +0 -0
  123. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/stepper_motor.py +0 -0
  124. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/uRFID.py +0 -0
  125. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/devices/uart_handler.py +0 -0
  126. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/__init__.py +0 -0
  127. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/audio.py +0 -0
  128. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/hardware.py +0 -0
  129. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/profiles.py +0 -0
  130. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/state_machine.py +0 -0
  131. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/timer.py +0 -0
  132. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/firmware/pycontrol/utility.py +0 -0
  133. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/fsutil.py +0 -0
  134. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/integrations/README.md +0 -0
  135. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/integrations/__init__.py +0 -0
  136. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/integrations/_queue.py +0 -0
  137. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/integrations/notify/__init__.py +0 -0
  138. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/integrations/notify/slack.py +0 -0
  139. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/integrations/notify/telegram.py +0 -0
  140. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/mcu.py +0 -0
  141. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/protocol/README.md +0 -0
  142. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/protocol/__init__.py +0 -0
  143. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/protocol/analog.py +0 -0
  144. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/protocol/decoder.py +0 -0
  145. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/protocol/encoder.py +0 -0
  146. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/protocol/messages.py +0 -0
  147. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/README.md +0 -0
  148. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/__init__.py +0 -0
  149. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/archive.py +0 -0
  150. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/collect.py +0 -0
  151. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/hashing.py +0 -0
  152. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/models.py +0 -0
  153. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/provenance/source.py +0 -0
  154. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/recording/README.md +0 -0
  155. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/recording/__init__.py +0 -0
  156. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/recording/analog_writer.py +0 -0
  157. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/recording/console.py +0 -0
  158. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/recording/event_rows.py +0 -0
  159. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/recording/native_event_logger.py +0 -0
  160. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/repl/README.md +0 -0
  161. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/repl/__init__.py +0 -0
  162. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/repl/device_helpers.py +0 -0
  163. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/repl/file_transfer.py +0 -0
  164. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/repl/json_repl.py +0 -0
  165. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/repl/raw_repl.py +0 -0
  166. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/README.md +0 -0
  167. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/__init__.py +0 -0
  168. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/board_session.py +0 -0
  169. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/device_resolution.py +0 -0
  170. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/info.py +0 -0
  171. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/link.py +0 -0
  172. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/phases.py +0 -0
  173. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/protocols.py +0 -0
  174. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/pump.py +0 -0
  175. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/rows.py +0 -0
  176. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/services/__init__.py +0 -0
  177. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/services/firmware.py +0 -0
  178. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/services/maintenance.py +0 -0
  179. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/services/setup.py +0 -0
  180. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/services/sync.py +0 -0
  181. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/types.py +0 -0
  182. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/session/variables.py +0 -0
  183. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/testing/__init__.py +0 -0
  184. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/testing/fake_board.py +0 -0
  185. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/testing/frames.py +0 -0
  186. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/transport/README.md +0 -0
  187. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/transport/__init__.py +0 -0
  188. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/transport/base.py +0 -0
  189. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/transport/mock.py +0 -0
  190. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/transport/replay.py +0 -0
  191. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/transport/serial.py +0 -0
  192. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace/README.md +0 -0
  193. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace/__init__.py +0 -0
  194. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace/creation.py +0 -0
  195. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace/hwdef_inspect.py +0 -0
  196. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace/task_inspect.py +0 -0
  197. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/__init__.py +0 -0
  198. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/controls/README.md +0 -0
  199. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/controls/blinker_gui.json +0 -0
  200. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/controls/example_dialog.py +0 -0
  201. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/experiment_handlers/README.md +0 -0
  202. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/experiment_handlers/notion_logger.py +0 -0
  203. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/experiment_handlers/s3_data_exporter.py +0 -0
  204. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/plotters/README.md +0 -0
  205. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/task_handlers/README.md +0 -0
  206. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/task_handlers/example_handler.py +0 -0
  207. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/task_handlers/run_notify.py +0 -0
  208. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/addons/task_handlers/trial_summary.py +0 -0
  209. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/devices/__init__.py +0 -0
  210. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/devices/example_device.py +0 -0
  211. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/experiments/blink_demo.json +0 -0
  212. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/settings.json +0 -0
  213. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/setups.json +0 -0
  214. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/src/pycontrol/workspace_template/tasks/examples/blinker-pattern-h7.py +0 -0
  215. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/CLASSIFICATION.md +0 -0
  216. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/__init__.py +0 -0
  217. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/contracts/test_wire_contract.py +0 -0
  218. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/firmware_stubs/__init__.py +0 -0
  219. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/firmware_stubs/machine.py +0 -0
  220. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/firmware_stubs/micropython.py +0 -0
  221. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/firmware_stubs/pyb.py +0 -0
  222. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/firmware_stubs/ucollections.py +0 -0
  223. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/firmware_stubs/ujson.py +0 -0
  224. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/__init__.py +0 -0
  225. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/frames/blinker_real.in.bin +0 -0
  226. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/frames/blinker_real.out.bin +0 -0
  227. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/frames/counter_demo_real.in.bin +0 -0
  228. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/frames/counter_demo_real.out.bin +0 -0
  229. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/frames/encoder_real.in.bin +0 -0
  230. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/frames/encoder_real.out.bin +0 -0
  231. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/replay.py +0 -0
  232. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/blinker_real.meta.json +0 -0
  233. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/blinker_real.tsv +0 -0
  234. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/counter_demo_real.meta.json +0 -0
  235. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/counter_demo_real.tsv +0 -0
  236. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/encoder_real.meta.json +0 -0
  237. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/encoder_real.tsv +0 -0
  238. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/encoder_real_encoder.data.npy +0 -0
  239. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/sessions/encoder_real_encoder.time.npy +0 -0
  240. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/tasks/blinker_real.task.py +0 -0
  241. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/tasks/counter_demo_real.task.py +0 -0
  242. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/golden/tasks/encoder_real.task.py +0 -0
  243. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/__init__.py +0 -0
  244. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/test_board_session.py +0 -0
  245. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/test_cli_workspace.py +0 -0
  246. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/test_experiment_runner.py +0 -0
  247. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/test_real_hardware_replay.py +0 -0
  248. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/test_repl.py +0 -0
  249. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/integration/test_run.py +0 -0
  250. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/__init__.py +0 -0
  251. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/__init__.py +0 -0
  252. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/conftest.py +0 -0
  253. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_analog_trigger_variables.py +0 -0
  254. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_audio.py +0 -0
  255. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_breakout_qwiic.py +0 -0
  256. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_device_helpers.py +0 -0
  257. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_devices_init.py +0 -0
  258. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_hardware_uses_profile.py +0 -0
  259. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_profiles.py +0 -0
  260. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_rotary_encoder.py +0 -0
  261. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_run_sequence.py +0 -0
  262. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_schmitt_trigger_threshold.py +0 -0
  263. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_state_machine_variables.py +0 -0
  264. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_timer_rounding.py +0 -0
  265. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_utility_log.py +0 -0
  266. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/firmware/test_vl53l4.py +0 -0
  267. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_addons.py +0 -0
  268. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_analysis_data_import.py +0 -0
  269. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_board_link.py +0 -0
  270. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_bundles_collect.py +0 -0
  271. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_bus_subscriber.py +0 -0
  272. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_cli.py +0 -0
  273. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_cli_setups.py +0 -0
  274. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_config_models.py +0 -0
  275. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_device_resolution.py +0 -0
  276. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_device_table_consistency.py +0 -0
  277. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_experiment_hook_sequencer.py +0 -0
  278. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_experiment_planning.py +0 -0
  279. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_file_transfer_errors.py +0 -0
  280. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_firmware_root.py +0 -0
  281. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_firmware_service.py +0 -0
  282. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_firmware_status.py +0 -0
  283. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_integrations_notify.py +0 -0
  284. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_maintenance_service.py +0 -0
  285. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_phases.py +0 -0
  286. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_protocol_decoder.py +0 -0
  287. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_protocol_encoder.py +0 -0
  288. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_provenance.py +0 -0
  289. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_public_api.py +0 -0
  290. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_raw_repl.py +0 -0
  291. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_recording.py +0 -0
  292. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_run_bundle_format.py +0 -0
  293. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_run_data_durability.py +0 -0
  294. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_run_recovery.py +0 -0
  295. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_serial_transport.py +0 -0
  296. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_session_pump.py +0 -0
  297. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_session_rows.py +0 -0
  298. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_session_variables.py +0 -0
  299. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_setup_identity.py +0 -0
  300. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_subject_id.py +0 -0
  301. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_supervisor_join.py +0 -0
  302. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_transport_mock.py +0 -0
  303. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_workspace.py +0 -0
  304. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_workspace_hwdef_inspect.py +0 -0
  305. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tests/unit/test_workspace_template.py +0 -0
  306. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tools/README.md +0 -0
  307. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tools/capture_fixtures.py +0 -0
  308. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tools/hardware_acceptance.py +0 -0
  309. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tools/maintainability_metrics.py +0 -0
  310. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/tools/run_tests.py +0 -0
  311. {pycontrol_core-3.0.0b2 → pycontrol_core-3.0.0b3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pycontrol-core
3
- Version: 3.0.0b2
3
+ Version: 3.0.0b3
4
4
  Summary: Sans-IO host library for the pyControl behavioural-experiment framework.
5
5
  Project-URL: Homepage, https://github.com/pyControl/core
6
6
  Author: pyControl contributors
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ### Styled status text
6
+
7
+ - `TaskHandler.update_status(...)` accepts a `StyledText(*parts)` value, drawn
8
+ as text whose `Span(text, color=None, bold=False)` parts are coloured or bold.
9
+ Its `StatusField` has `kind == "styled"` and the spans in the new `spans`
10
+ attribute; `value` is still the plain text.
11
+
5
12
  ### Breakout naming
6
13
 
7
14
  - Breakout filenames and classes now identify port count, PCB revision, and
@@ -39,7 +39,9 @@ from pycontrol.addons import (
39
39
  Log,
40
40
  Print,
41
41
  Progress,
42
+ Span,
42
43
  State,
44
+ StyledText,
43
45
  TaskHandler,
44
46
  Var,
45
47
  )
@@ -47,8 +49,9 @@ from pycontrol.addons import (
47
49
 
48
50
  See `docs/task-handlers.md`. A task handler publishes frontend status with
49
51
  `update_status(...)` (a mapping of labels to values, where a `Progress` value
50
- draws a bar) and reports problems with the diagnostics helpers; it does not
51
- reach into the subscriber registry. Its `set_variable()` and `trigger_event()`
52
+ draws a bar and a `StyledText` value draws coloured or bold `Span`s) and
53
+ reports problems with the diagnostics helpers; it does not reach into the
54
+ subscriber registry. Its `set_variable()` and `trigger_event()`
52
55
  are recorded with source `task_handler`.
53
56
 
54
57
  ### Experiment handlers
@@ -109,6 +112,7 @@ from pycontrol.bus import (
109
112
  SessionEnded,
110
113
  SessionEndReason,
111
114
  SessionStarted,
115
+ Span,
112
116
  StatusField,
113
117
  Subscriber,
114
118
  TaskStatusUpdated,
@@ -126,10 +130,11 @@ A subscriber implements any of `on_session_start`, `on_data`,
126
130
  `TaskStatusUpdated` is emitted by host-side task handlers through
127
131
  `TaskHandler.update_status(...)` and forwarded through the normal subscriber
128
132
  registry. Its `fields` are `StatusField` records (`label`, `value`, `kind`,
129
- `fraction`, `fill_color`, `text_color`). `value` is always a display string, so a
133
+ `fraction`, `fill_color`, `text_color`, `spans`). `value` is always a display string, so a
130
134
  consumer that only handles `kind == "text"` renders every field correctly; a
131
- field is `kind == "progress"` when the task handler passed a `Progress` value.
132
- `clean_color` is the colour check frontends apply before styling a bar.
135
+ field is `kind == "progress"` when the task handler passed a `Progress` value,
136
+ and `kind == "styled"`, with its `Span`s in `spans`, for a `StyledText` value.
137
+ `clean_color` is the colour check frontends apply before styling a bar or span.
133
138
 
134
139
  `SessionEnded.reason` (a `SessionEndReason` literal: `"stopped"`,
135
140
  `"task_ended"`, `"task_error"`, `"connection_lost"`, `"pump_crashed"`) is the
@@ -214,6 +214,16 @@ class WaterBudget(TaskHandler):
214
214
  })
215
215
  ```
216
216
 
217
+ A `StyledText` value is drawn as text with coloured or bold runs. Each part is a
218
+ plain string or a `Span(text, color=None, bold=False)`, with colours checked like
219
+ `Progress`'s:
220
+
221
+ ```python
222
+ from pycontrol.addons import Span, StyledText
223
+
224
+ self.update_status({"Side": StyledText("last: ", Span("L", color="#FF9A00", bold=True))})
225
+ ```
226
+
217
227
  Every field also carries a plain display string, so a frontend that only renders
218
228
  text still shows the value, and the run bundle records `label: "0.75 / 1.50 mL"`
219
229
  either way.
@@ -1,5 +1,5 @@
1
1
  """pycontrol host library: sans-IO core + addon points."""
2
2
 
3
- __version__ = "3.0.0b2"
3
+ __version__ = "3.0.0b3"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -5,6 +5,8 @@ A minimal, Qt-free contract for user code to plug into a session:
5
5
  - :class:`TaskHandler` -- subscriber base that converts native row batches
6
6
  into ergonomic ``State``/``Event``/``Var``/``Analog`` namedtuples.
7
7
  - :class:`Progress` -- a live status value a frontend draws as a progress bar.
8
+ - :class:`StyledText` and :class:`Span` -- a live status value drawn as text
9
+ with coloured or bold runs.
8
10
  - :class:`ExperimentHandler` -- host-side experiment lifecycle callbacks.
9
11
  - :class:`TaskControlsContext` -- Qt-free runtime API passed to custom task
10
12
  controls built by a frontend.
@@ -29,7 +31,7 @@ from pycontrol.addons.loader import (
29
31
  loaded_module_for_path,
30
32
  )
31
33
  from pycontrol.addons.task_api import Analog, Event, Log, Print, State, TaskHandler, Var
32
- from pycontrol.bus.status import Progress
34
+ from pycontrol.bus.status import Progress, Span, StyledText
33
35
 
34
36
  __all__ = [
35
37
  "AddonDiscoveryFailure",
@@ -40,7 +42,9 @@ __all__ = [
40
42
  "Log",
41
43
  "Print",
42
44
  "Progress",
45
+ "Span",
43
46
  "State",
47
+ "StyledText",
44
48
  "TaskControlsContext",
45
49
  "TaskControlsDefinition",
46
50
  "TaskControlsWidgetBuilder",
@@ -16,7 +16,7 @@ from pycontrol.bus.events import (
16
16
  TaskStatusUpdated,
17
17
  Warning,
18
18
  )
19
- from pycontrol.bus.status import Progress, StatusField, clean_color, normalise_status_fields
19
+ from pycontrol.bus.status import Progress, Span, StatusField, StyledText, clean_color, normalise_status_fields
20
20
  from pycontrol.bus.subscriber import Subscriber, SubscriberRegistry
21
21
 
22
22
  __all__ = [
@@ -28,7 +28,9 @@ __all__ = [
28
28
  "SessionEndReason",
29
29
  "SessionEnded",
30
30
  "SessionStarted",
31
+ "Span",
31
32
  "StatusField",
33
+ "StyledText",
32
34
  "Subscriber",
33
35
  "SubscriberRegistry",
34
36
  "TaskStatusUpdated",
@@ -4,7 +4,8 @@ A task handler publishes a snapshot as a plain mapping of label to value.
4
4
  Values are normalised here into :class:`StatusField` records so every frontend
5
5
  renders the same thing, and so a richer field kind (a progress bar) can travel
6
6
  on the bus without breaking a consumer that only understands text: ``value`` is
7
- always the display string, whatever the kind.
7
+ always the display string, whatever the kind. Richer kinds are a progress bar
8
+ and styled text with coloured or bold spans.
8
9
  """
9
10
 
10
11
  from __future__ import annotations
@@ -16,6 +17,7 @@ from typing import Any
16
17
 
17
18
  TEXT = "text"
18
19
  PROGRESS = "progress"
20
+ STYLED = "styled"
19
21
 
20
22
  # Frontends may paste these into a stylesheet, so only simple colour forms pass.
21
23
  _COLOR_RE = re.compile(
@@ -42,6 +44,34 @@ class Progress:
42
44
  text_color: str | None = None
43
45
 
44
46
 
47
+ @dataclass(frozen=True, slots=True)
48
+ class Span:
49
+ """One run of :class:`StyledText`: ``text`` drawn in ``color``, optionally bold.
50
+
51
+ ``color`` is a CSS colour like :class:`Progress`'s; anything else is ignored.
52
+ """
53
+
54
+ text: str
55
+ color: str | None = None
56
+ bold: bool = False
57
+
58
+
59
+ @dataclass(frozen=True, slots=True, init=False)
60
+ class StyledText:
61
+ """A status value a frontend renders as text with coloured or bold runs.
62
+
63
+ Each part is a plain string or a :class:`Span`::
64
+
65
+ StyledText("Block ", Span("L", color="#FF9A00", bold=True))
66
+ """
67
+
68
+ spans: tuple[Span, ...]
69
+
70
+ def __init__(self, *parts: str | Span) -> None:
71
+ spans = tuple(part if isinstance(part, Span) else Span(str(part)) for part in parts)
72
+ object.__setattr__(self, "spans", spans)
73
+
74
+
45
75
  @dataclass(frozen=True, slots=True)
46
76
  class StatusField:
47
77
  """One normalised field of a live status snapshot."""
@@ -52,6 +82,7 @@ class StatusField:
52
82
  fraction: float | None = None
53
83
  fill_color: str | None = None
54
84
  text_color: str | None = None
85
+ spans: tuple[Span, ...] = ()
55
86
 
56
87
 
57
88
  def normalise_status_fields(status: Mapping[str, Any]) -> tuple[StatusField, ...]:
@@ -74,6 +105,8 @@ def clean_color(color: object) -> str | None:
74
105
  def _status_field(label: str, value: Any) -> StatusField:
75
106
  if isinstance(value, Progress):
76
107
  return _progress_field(label, value)
108
+ if isinstance(value, StyledText):
109
+ return _styled_field(label, value)
77
110
  return StatusField(label=label, value="" if value is None else str(value))
78
111
 
79
112
 
@@ -97,10 +130,25 @@ def _progress_field(label: str, progress: Progress) -> StatusField:
97
130
  )
98
131
 
99
132
 
133
+ def _styled_field(label: str, styled: StyledText) -> StatusField:
134
+ spans = tuple(Span(str(span.text), clean_color(span.color), bool(span.bold)) for span in styled.spans)
135
+ return StatusField(label=label, value="".join(span.text for span in spans), kind=STYLED, spans=spans)
136
+
137
+
100
138
  def _format_number(value: float) -> str:
101
139
  if value == int(value):
102
140
  return str(int(value))
103
141
  return f"{value:.3f}".rstrip("0").rstrip(".")
104
142
 
105
143
 
106
- __all__ = ["PROGRESS", "TEXT", "Progress", "StatusField", "clean_color", "normalise_status_fields"]
144
+ __all__ = [
145
+ "PROGRESS",
146
+ "STYLED",
147
+ "TEXT",
148
+ "Progress",
149
+ "Span",
150
+ "StatusField",
151
+ "StyledText",
152
+ "clean_color",
153
+ "normalise_status_fields",
154
+ ]
@@ -6,7 +6,7 @@ from . import state_machine as sm
6
6
  from . import hardware as hw
7
7
  from . import utility as ut
8
8
 
9
- VERSION = "3.0.0b2"
9
+ VERSION = "3.0.0b3"
10
10
 
11
11
 
12
12
  class FrameworkError(BaseException): # Exception for pycontrol framework errors.
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import pytest
6
6
 
7
- from pycontrol.bus.status import Progress, StatusField, clean_color, normalise_status_fields
7
+ from pycontrol.bus.status import STYLED, Progress, Span, StatusField, StyledText, clean_color, normalise_status_fields
8
8
 
9
9
 
10
10
  def test_plain_values_become_text_fields() -> None:
@@ -71,3 +71,17 @@ def test_a_rejected_color_leaves_the_field_unstyled() -> None:
71
71
  [status_field] = normalise_status_fields({"Bar": Progress(1, 2, fill_color="}; evil")})
72
72
 
73
73
  assert status_field.fill_color is None
74
+
75
+
76
+ def test_styled_text_keeps_spans_and_a_plain_display_string() -> None:
77
+ [status_field] = normalise_status_fields({"Block": StyledText("1X ", Span("L", color="#FF9A00", bold=True))})
78
+
79
+ assert status_field.kind == STYLED
80
+ assert status_field.value == "1X L"
81
+ assert status_field.spans == (Span("1X "), Span("L", color="#FF9A00", bold=True))
82
+
83
+
84
+ def test_styled_text_drops_rejected_span_colors() -> None:
85
+ [status_field] = normalise_status_fields({"Block": StyledText(Span("L", color="red; } * { x"))})
86
+
87
+ assert status_field.spans == (Span("L"),)