mpflash 1.28.1.dev2__tar.gz → 1.28.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 (241) hide show
  1. mpflash-1.28.2/.env +43 -0
  2. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/workflows/copilot-setup-steps.yml +1 -1
  3. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/workflows/pytest_mpflash.yml +15 -10
  4. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/workflows/release.yml +6 -6
  5. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.vscode/settings.json +9 -2
  6. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/PKG-INFO +8 -1
  7. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/contributing.md +22 -11
  8. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/developer.md +35 -14
  9. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/stm32_udev_rules.md +38 -15
  10. mpflash-1.28.2/justfile +87 -0
  11. mpflash-1.28.2/mpflash/bootloader/activate.py +118 -0
  12. mpflash-1.28.2/mpflash/bootloader/base.py +55 -0
  13. mpflash-1.28.2/mpflash/bootloader/builtins/__init__.py +10 -0
  14. {mpflash-1.28.1.dev2/mpflash/bootloader → mpflash-1.28.2/mpflash/bootloader/builtins}/manual.py +17 -2
  15. mpflash-1.28.2/mpflash/bootloader/builtins/mpy.py +28 -0
  16. {mpflash-1.28.1.dev2/mpflash/bootloader → mpflash-1.28.2/mpflash/bootloader/builtins}/touch1200.py +17 -5
  17. mpflash-1.28.2/mpflash/bootloader/detect.py +70 -0
  18. mpflash-1.28.2/mpflash/bootloader/registry.py +115 -0
  19. mpflash-1.28.2/mpflash/build.py +576 -0
  20. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/cli_flash.py +171 -12
  21. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/cli_main.py +14 -0
  22. mpflash-1.28.2/mpflash/cli_plugins.py +64 -0
  23. mpflash-1.28.2/mpflash/cli_pyocd.py +248 -0
  24. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/common.py +39 -2
  25. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/custom/naming.py +1 -3
  26. mpflash-1.28.2/mpflash/flash/__init__.py +319 -0
  27. mpflash-1.28.2/mpflash/flash/base.py +151 -0
  28. mpflash-1.28.2/mpflash/flash/builtins/__init__.py +18 -0
  29. mpflash-1.28.1.dev2/mpflash/flash/stm32.py → mpflash-1.28.2/mpflash/flash/builtins/dfu/__init__.py +7 -1
  30. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins/dfu}/stm32_dfu.py +26 -7
  31. mpflash-1.28.2/mpflash/flash/builtins/dfu_backend.py +119 -0
  32. mpflash-1.28.1.dev2/mpflash/flash/esp.py → mpflash-1.28.2/mpflash/flash/builtins/esp/__init__.py +7 -3
  33. mpflash-1.28.2/mpflash/flash/builtins/esptool_backend.py +50 -0
  34. mpflash-1.28.2/mpflash/flash/builtins/pyocd/__init__.py +5 -0
  35. mpflash-1.28.2/mpflash/flash/builtins/pyocd/core.py +787 -0
  36. mpflash-1.28.2/mpflash/flash/builtins/pyocd/flash.py +724 -0
  37. mpflash-1.28.2/mpflash/flash/builtins/pyocd/probes.py +111 -0
  38. mpflash-1.28.2/mpflash/flash/builtins/pyocd_backend.py +90 -0
  39. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins}/uf2/__init__.py +28 -48
  40. mpflash-1.28.2/mpflash/flash/builtins/uf2/erase.py +65 -0
  41. mpflash-1.28.2/mpflash/flash/builtins/uf2/volume.py +101 -0
  42. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins}/uf2/windows.py +12 -5
  43. mpflash-1.28.2/mpflash/flash/builtins/uf2/wsl2.py +70 -0
  44. mpflash-1.28.2/mpflash/flash/builtins/uf2_backend.py +91 -0
  45. mpflash-1.28.2/mpflash/flash/context.py +72 -0
  46. mpflash-1.28.2/mpflash/flash/registry.py +154 -0
  47. mpflash-1.28.2/mpflash/flash/services.py +96 -0
  48. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/flash/worklist.py +59 -5
  49. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/list.py +4 -2
  50. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpboard_id/known.py +19 -0
  51. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpremoteboard/__init__.py +47 -46
  52. mpflash-1.28.2/mpflash/mpremoteboard/erase_bdev.py +93 -0
  53. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpremoteboard/mpy_fw_info.py +1 -0
  54. mpflash-1.28.2/mpflash/udev_rules/49-stlinkv2-1.rules +13 -0
  55. mpflash-1.28.2/mpflash/udev_rules/49-stlinkv2.rules +10 -0
  56. mpflash-1.28.2/mpflash/udev_rules/49-stlinkv3.rules +27 -0
  57. mpflash-1.28.2/mpflash/udev_rules/49-vtlinkii.rules +3 -0
  58. mpflash-1.28.2/mpflash/udev_rules/49-wch-link.rules +19 -0
  59. mpflash-1.28.2/mpflash/udev_rules/50-cmsis-dap.rules +63 -0
  60. mpflash-1.28.2/mpflash/udev_rules/50-picoprobe.rules +12 -0
  61. mpflash-1.28.2/mpflash/udev_rules/65-mpflash-stm32-dfu.rules +4 -0
  62. mpflash-1.28.2/mpflash/udev_rules/README.md +54 -0
  63. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/vendor/readme.md +1 -4
  64. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/pyproject.toml +28 -1
  65. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/basicgit_test.py +1 -1
  66. mpflash-1.28.2/tests/bootloader/test_bootloader_detect.py +55 -0
  67. mpflash-1.28.2/tests/bootloader/test_bootloader_registry.py +85 -0
  68. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/cli/test_cli_download.py +1 -1
  69. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/cli/test_cli_flash.py +197 -2
  70. mpflash-1.28.2/tests/cli/test_cli_list.py +86 -0
  71. mpflash-1.28.2/tests/cli/test_cli_plugins.py +56 -0
  72. mpflash-1.28.2/tests/cli/test_cli_pyocd.py +156 -0
  73. mpflash-1.28.2/tests/conftest.py +268 -0
  74. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/test_core.py +1 -1
  75. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/test_loader.py +1 -1
  76. mpflash-1.28.2/tests/fixtures/mock_pyocd_data.py +167 -0
  77. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_boot_touch1200.py +4 -4
  78. mpflash-1.28.2/tests/flash/test_dfu_backend.py +117 -0
  79. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_flash_1.py +62 -12
  80. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_flash_esp.py +5 -5
  81. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_flash_tasks.py +183 -0
  82. mpflash-1.28.2/tests/flash/test_flash_uf2_A.py +154 -0
  83. mpflash-1.28.2/tests/flash/test_registry.py +334 -0
  84. mpflash-1.28.2/tests/flash/test_services.py +41 -0
  85. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_stm32_dfu.py +43 -43
  86. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_uf2_boardid.py +1 -1
  87. mpflash-1.28.2/tests/flash/test_uf2_erase.py +122 -0
  88. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_uf2_linux.py +3 -3
  89. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_uf2_mac.py +1 -1
  90. mpflash-1.28.2/tests/flash/test_uf2_volume.py +67 -0
  91. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_uf2_windows.py +4 -4
  92. mpflash-1.28.2/tests/flash/test_uf2_wsl2.py +65 -0
  93. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/flash/test_worklist_refactored.py +71 -0
  94. mpflash-1.28.2/tests/hw/conftest.py +162 -0
  95. mpflash-1.28.2/tests/hw/test_hw_backends.py +68 -0
  96. mpflash-1.28.2/tests/hw/test_hw_erase.py +52 -0
  97. mpflash-1.28.2/tests/integration/test_cli_integration.py +430 -0
  98. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/mpboard_id/test_known_boards.py +16 -1
  99. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/mpremoteboard/test_mprb.py +89 -1
  100. mpflash-1.28.2/tests/test_build.py +456 -0
  101. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_list.py +13 -0
  102. mpflash-1.28.2/tests/unit/test_probe_management.py +523 -0
  103. mpflash-1.28.2/tests/unit/test_pyocd_flash_coverage.py +604 -0
  104. mpflash-1.28.2/tests/unit/test_target_detection.py +431 -0
  105. mpflash-1.28.2/tests.md +180 -0
  106. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/uv.lock +451 -125
  107. mpflash-1.28.1.dev2/.env +0 -22
  108. mpflash-1.28.1.dev2/justfile +0 -48
  109. mpflash-1.28.1.dev2/mpflash/bootloader/activate.py +0 -60
  110. mpflash-1.28.1.dev2/mpflash/bootloader/detect.py +0 -84
  111. mpflash-1.28.1.dev2/mpflash/bootloader/micropython.py +0 -12
  112. mpflash-1.28.1.dev2/mpflash/flash/__init__.py +0 -83
  113. mpflash-1.28.1.dev2/mpflash/vendor/pico-universal-flash-nuke/LICENSE.txt +0 -21
  114. mpflash-1.28.1.dev2/mpflash/vendor/pico-universal-flash-nuke/universal_flash_nuke.uf2 +0 -0
  115. mpflash-1.28.1.dev2/tests/cli/test_cli_list.py +0 -42
  116. mpflash-1.28.1.dev2/tests/conftest.py +0 -125
  117. mpflash-1.28.1.dev2/tests/flash/test_flash_uf2_A.py +0 -177
  118. mpflash-1.28.1.dev2/tests.md +0 -35
  119. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.all-contributorsrc +0 -0
  120. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/CODEOWNERS +0 -0
  121. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/codecov.yml +0 -0
  122. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/copilot-instructions.md +0 -0
  123. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/dependabot.yml +0 -0
  124. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.github/workflows/dependency-review.yml +0 -0
  125. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.gitignore +0 -0
  126. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.mailmap +0 -0
  127. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.readthedocs.yaml +0 -0
  128. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.vscode/extensions.json +0 -0
  129. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.vscode/launch.json +0 -0
  130. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.vscode/micropython-dict.txt +0 -0
  131. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/.vscode/tasks.json +0 -0
  132. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/LICENSE +0 -0
  133. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/README.md +0 -0
  134. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/OIG4.jpg +0 -0
  135. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/api-reference.md +0 -0
  136. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/architecture.md +0 -0
  137. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/custom_firmware_prd.md +0 -0
  138. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/index.md +0 -0
  139. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/logger_fixes.md +0 -0
  140. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/mpflash-2 (Small).jpg +0 -0
  141. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/mpflash-2.jpg +0 -0
  142. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/mpflash.jpg +0 -0
  143. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/mpflash_api_example.ipynb +0 -0
  144. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/mpremoteboard_api.md +0 -0
  145. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/startup_speed.md +0 -0
  146. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/docs/troubleshooting.md +0 -0
  147. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/examples/worklist_api_improvements.py +0 -0
  148. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/__init__.py +0 -0
  149. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/ask_input.py +0 -0
  150. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/basicgit.py +0 -0
  151. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/bootloader/__init__.py +0 -0
  152. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/cli_add.py +0 -0
  153. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/cli_download.py +0 -0
  154. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/cli_group.py +0 -0
  155. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/cli_list.py +0 -0
  156. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/config.py +0 -0
  157. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/connected.py +0 -0
  158. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/custom/__init__.py +0 -0
  159. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/custom/add.py +0 -0
  160. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/custom/copy.py +0 -0
  161. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/__init__.py +0 -0
  162. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/boards_version.txt +0 -0
  163. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/core.py +0 -0
  164. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/gather_boards.py +0 -0
  165. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/loader.py +0 -0
  166. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/meta.py +0 -0
  167. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/micropython_boards.zip +0 -0
  168. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/models.py +0 -0
  169. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/db/tools.py +0 -0
  170. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/download/__init__.py +0 -0
  171. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/download/from_web.py +0 -0
  172. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/download/fwinfo.py +0 -0
  173. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/download/jid.py +0 -0
  174. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/downloaded.py +0 -0
  175. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/errors.py +0 -0
  176. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins}/uf2/boardid.py +0 -0
  177. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins}/uf2/linux.py +0 -0
  178. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins}/uf2/macos.py +0 -0
  179. {mpflash-1.28.1.dev2/mpflash/flash → mpflash-1.28.2/mpflash/flash/builtins}/uf2/uf2disk.py +0 -0
  180. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/logger.py +0 -0
  181. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpboard_id/__init__.py +0 -0
  182. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpboard_id/alternate.py +0 -0
  183. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpboard_id/board_id.py +0 -0
  184. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpboard_id/resolve.py +0 -0
  185. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/mpremoteboard/runner.py +0 -0
  186. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/py.typed +0 -0
  187. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/vendor/board_database.py +0 -0
  188. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/vendor/click_aliases.py +0 -0
  189. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/vendor/dfu.py +0 -0
  190. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/vendor/pydfu.py +0 -0
  191. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash/versions.py +0 -0
  192. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/mpflash.code-workspace +0 -0
  193. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/reporting/MicroPython_boards.ipynb +0 -0
  194. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/reporting/build_reporting_db.py +0 -0
  195. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/reporting/micropython_boards_reporting.sqlite3 +0 -0
  196. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Qry_board_id_to_versions.sqlite3-query +0 -0
  197. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-1.ipynb +0 -0
  198. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-1.sqlite3-query +0 -0
  199. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-2.sqlite3-query +0 -0
  200. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-3.sqlite3-query +0 -0
  201. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-4.sqlite3-query +0 -0
  202. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-6.sqlite3-query +0 -0
  203. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-7.sqlite3-query +0 -0
  204. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/Untitled-8.sqlite3-query +0 -0
  205. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/join_boards-downloads.sqlite3-query +0 -0
  206. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/libusb_flash.ipynb +0 -0
  207. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/micropython_boards.zip +0 -0
  208. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/mpr_command.ipynb +0 -0
  209. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/publish.py +0 -0
  210. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/scripts/sqlite3.exe +0 -0
  211. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/src/mpflash/db/gather_boards.py +0 -0
  212. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/basicgit/test_basicgit.py +0 -0
  213. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/cli/test_cli_options.py +0 -0
  214. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/custom/test_init.py +0 -0
  215. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/custom/test_naming.py +0 -0
  216. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/data/firmware.jsonl +0 -0
  217. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/data/mpflash.db +0 -0
  218. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/conftest.py +0 -0
  219. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/test_find_board_id.py +0 -0
  220. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/test_gather.py +0 -0
  221. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/test_meta.py +0 -0
  222. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/db/test_tools.py +0 -0
  223. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/mpboard_id/test_alternate.py +0 -0
  224. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/mpremoteboard/fake_output.py +0 -0
  225. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/mpremoteboard/fake_reset.py +0 -0
  226. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/mpremoteboard/fake_slow_output.py +0 -0
  227. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_ask_input.py +0 -0
  228. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_cli_add.py +0 -0
  229. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_cli_main.py +0 -0
  230. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_common.py +0 -0
  231. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_config.py +0 -0
  232. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_connected.py +0 -0
  233. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_download.py +0 -0
  234. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_downloaded.py +0 -0
  235. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_filtered_comports.py +0 -0
  236. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_gather_boards_additional.py +0 -0
  237. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_logger_fix.py +0 -0
  238. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_mp_board_filter.py +0 -0
  239. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_mpf_api.py +0 -0
  240. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_version_preservation_fix.py +0 -0
  241. {mpflash-1.28.1.dev2 → mpflash-1.28.2}/tests/test_versions.py +0 -0
mpflash-1.28.2/.env ADDED
@@ -0,0 +1,43 @@
1
+ # Python .env file ..
2
+
3
+ # PYTHONPYCACHEPREFIX=__my_pycache
4
+
5
+ JUPYTER_PLATFORM_DIRS=1
6
+ PYDEVD_DISABLE_FILE_VALIDATION=1
7
+ # Allow tests to find sources
8
+ PYTHONPATH = src
9
+
10
+ # give 20 secs to complete : subprocess.getstatusoutput
11
+ PYDEVD_WARN_EVALUATION_TIMEOUT = 20
12
+
13
+ # stop nagging for updates
14
+ PIP_DISABLE_PIP_VERSION_CHECK=1
15
+
16
+ # stop neggin during debug
17
+ JUPYTER_PLATFORM_DIRS=1
18
+ PYDEVD_DISABLE_FILE_VALIDATION=1
19
+
20
+
21
+ MPFLASH_FIRMWARE=./scratch
22
+ MICROPY_DIR=~/micropython
23
+
24
+ # -----------------------------------------------------------------------------------------------
25
+ # HIL testing
26
+ # -----------------------------------------------------------------------------------------------
27
+ # UF2 - RPI Pico2 on WSL
28
+ # MPFLASH_HW_UF2_PORT=/dev/ttyACM1
29
+ # MPFLASH_HW_UF2_FW=/mnt/c/Users/jos_v/Downloads/firmware/rp2/RPI_PICO2-v1.28.0.uf2
30
+
31
+ # # DFU - PYBV11 on WSL
32
+ # MPFLASH_HW_DFU_PORT=/dev/ttyACM0
33
+ # MPFLASH_HW_DFU_FW=/mnt/c/Users/jos_v/Downloads/firmware/stm32/PYBV11-DP-v1.28.0.dfu
34
+
35
+ # # PYOCD - PYBV11 on WSL
36
+ # # MPFLASH_HW_PYOCD_PROBE=066CFF514949877187244446
37
+ # MPFLASH_HW_PYOCD_PROBE=auto
38
+ # MPFLASH_HW_PYOCD_PORT=/dev/ttyACM1
39
+ # MPFLASH_HW_PYOCD_FW=/mnt/c/Users/jos_v/Downloads/firmware/stm32/PYBV11-DP-v1.28.0.hex
40
+
41
+ # # esptool - ESP32 on WSL
42
+ # MPFLASH_HW_ESP_PORT=/dev/ttyUSB0
43
+ # MPFLASH_HW_ESP_FW=/mnt/c/Users/jos_v/Downloads/firmware/esp32/ESP32_GENERIC-SPIRAM-v1.28.0.bin
@@ -26,7 +26,7 @@ jobs:
26
26
  # If you do not check out your code, Copilot will do this for you.
27
27
  steps:
28
28
  - name: Install the latest version of uv
29
- uses: astral-sh/setup-uv@v8.2.0
29
+ uses: astral-sh/setup-uv@v7
30
30
  with:
31
31
  version: "latest"
32
32
 
@@ -53,12 +53,12 @@ jobs:
53
53
  # uv is not in the default image
54
54
  #----------------------------------------------
55
55
  - name: Install the latest version of uv
56
- uses: astral-sh/setup-uv@v8.2.0
56
+ uses: astral-sh/setup-uv@v7
57
57
  with:
58
58
  version: "latest"
59
59
 
60
60
  - name: Set up Python ${{ matrix.python-version }}
61
- uses: actions/setup-python@v6
61
+ uses: actions/setup-python@v7
62
62
  with:
63
63
  python-version: ${{ matrix.python-version }}
64
64
  # cache: "poetry"
@@ -66,14 +66,14 @@ jobs:
66
66
  #----------------------------------------------
67
67
  # install project
68
68
  #----------------------------------------------
69
- - name: Install mpflash & test dependencies
70
- run: uv sync --extra test
69
+ - name: Install mpflash & test dependencies (all extras)
70
+ run: uv sync --all-extras
71
71
 
72
72
  - name: Test mpflash
73
73
  run: |
74
74
  # run once to bootstrap database (test hack)
75
75
  uv run mpflash download --board RPI_PICO
76
- uv run pytest --cov --cov-branch --cov-report=xml
76
+ b
77
77
 
78
78
  #----------------------------------------------
79
79
  # upload coverage stats
@@ -81,13 +81,18 @@ jobs:
81
81
  #----------------------------------------------
82
82
 
83
83
  - name: Upload coverage-mpflash-*.xml to Codecov
84
- if: always() # ignore errors
84
+ if: ${{ !cancelled() }}
85
85
  continue-on-error: true
86
86
  uses: codecov/codecov-action@v7
87
87
  with:
88
- token: ${{ secrets.CODECOV_TOKEN }}
89
- # fail_ci_if_error: false
90
- # file: results/coverage-mpflash-${{ matrix.python-version }}-${{ matrix.os }}.xml
91
- # flags: mpflash
88
+ token: ${{ secrets.CODECOV_TOKEN }}
89
+ fail_ci_if_error: false
92
90
 
91
+ - name: Upload test results to Codecov
92
+ if: ${{ !cancelled() }}
93
+ uses: codecov/test-results-action@v1
94
+ with:
95
+ token: ${{ secrets.CODECOV_TOKEN }}
96
+ files: results/junit.xml
97
+ fail_ci_if_error: false
93
98
 
@@ -11,10 +11,10 @@ jobs:
11
11
  build:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@v7
15
15
 
16
16
  - name: Install uv
17
- uses: astral-sh/setup-uv@v6
17
+ uses: astral-sh/setup-uv@v7
18
18
  with:
19
19
  python-version: "3.14"
20
20
  enable-cache: true
@@ -23,7 +23,7 @@ jobs:
23
23
  run: uv build
24
24
 
25
25
  - name: Upload distribution artefacts
26
- uses: actions/upload-artifact@v4
26
+ uses: actions/upload-artifact@v7
27
27
  with:
28
28
  name: dist
29
29
  path: dist/
@@ -42,7 +42,7 @@ jobs:
42
42
  id-token: write
43
43
  steps:
44
44
  - name: Download distribution artefacts
45
- uses: actions/download-artifact@v4
45
+ uses: actions/download-artifact@v8
46
46
  with:
47
47
  name: dist
48
48
  path: dist/
@@ -56,13 +56,13 @@ jobs:
56
56
  permissions:
57
57
  contents: write
58
58
  steps:
59
- - uses: actions/checkout@v4
59
+ - uses: actions/checkout@v7
60
60
  with:
61
61
  # Full history so --generate-notes can resolve the previous tag.
62
62
  fetch-depth: 0
63
63
 
64
64
  - name: Download distribution artefacts
65
- uses: actions/download-artifact@v4
65
+ uses: actions/download-artifact@v8
66
66
  with:
67
67
  name: dist
68
68
  path: dist/
@@ -36,13 +36,20 @@
36
36
  "statusBar.background": "#b30a60",
37
37
  "statusBar.foreground": "#e7e7e7",
38
38
  "statusBarItem.hoverBackground": "#e30d7a",
39
- "statusBarItem.remoteBackground": "#b30a60",
39
+ "statusBarItem.remoteBackground": "#101e02",
40
40
  "statusBarItem.remoteForeground": "#e7e7e7",
41
41
  "tab.activeBorder": "#e30d7a",
42
42
  "titleBar.activeBackground": "#b30a60",
43
43
  "titleBar.activeForeground": "#e7e7e7",
44
44
  "titleBar.inactiveBackground": "#b30a6099",
45
- "titleBar.inactiveForeground": "#e7e7e799"
45
+ "titleBar.inactiveForeground": "#e7e7e799",
46
+ "activityBarTop.activeBackground": "#e30d7a",
47
+ "activityBarTop.background": "#e30d7a",
48
+ "activityBarTop.foreground": "#e7e7e7",
49
+ "activityBarTop.inactiveForeground": "#e7e7e799",
50
+ "commandCenter.foreground": "#e7e7e7",
51
+ "statusBar.debuggingBackground": "#b30a60",
52
+ "statusBar.debuggingForeground": "#e7e7e7"
46
53
  },
47
54
  "python.terminal.activateEnvInCurrentTerminal": true,
48
55
  "todo-tree.filtering.ignoreGitSubmodules": true,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpflash
3
- Version: 1.28.1.dev2
3
+ Version: 1.28.2
4
4
  Summary: Flash and download tool for MicroPython firmwares
5
5
  Project-URL: Homepage, https://github.com/Josverl/mpflash
6
6
  Project-URL: Documentation, https://github.com/Josverl/mpflash/blob/main/README.md
@@ -40,6 +40,7 @@ Requires-Dist: platformdirs>=4.2.0
40
40
  Requires-Dist: psutil<8.0.0,>=7.0.0
41
41
  Requires-Dist: pygithub>=2.1.1
42
42
  Requires-Dist: pytest-cov>=7.0.0
43
+ Requires-Dist: python-dotenv>=1.0.0
43
44
  Requires-Dist: pyusb>=1.2.1
44
45
  Requires-Dist: pywin32>=310; sys_platform == 'win32'
45
46
  Requires-Dist: questionary>=2.0.0
@@ -48,8 +49,14 @@ Requires-Dist: rich-click>=1.8.1
48
49
  Requires-Dist: tenacity==9.1.4
49
50
  Requires-Dist: tomli-w>=1.2.0
50
51
  Requires-Dist: tomli>=2.2.1
52
+ Provides-Extra: build
53
+ Requires-Dist: mpbuild<1.0.0,>=0.5.0; (python_version < '3.12') and extra == 'build'
54
+ Requires-Dist: mpbuild>=1.0.0; (python_version >= '3.12') and extra == 'build'
51
55
  Provides-Extra: dev
52
56
  Requires-Dist: keyring>=25.6.0; extra == 'dev'
57
+ Provides-Extra: pyocd
58
+ Requires-Dist: libusb-package==1.0.26.1; (python_version >= '3.14') and extra == 'pyocd'
59
+ Requires-Dist: pyocd>=0.44.1; extra == 'pyocd'
53
60
  Provides-Extra: test
54
61
  Requires-Dist: coverage<8.0.0,>=6.4.3; extra == 'test'
55
62
  Requires-Dist: distro>=1.8.0; extra == 'test'
@@ -527,18 +527,28 @@ To add support for a new hardware platform:
527
527
 
528
528
  ## Release Process
529
529
 
530
- ### Version Bumping
530
+ ### Version Bumping & Tagging
531
531
 
532
- MPFlash uses semantic versioning:
532
+ MPFlash uses semantic versioning and releases are automated via GitHub Actions.
533
+ Pushing a `v*` tag triggers [`.github/workflows/release.yml`](../.github/workflows/release.yml),
534
+ which builds the sdist + wheel, publishes to PyPI (trusted publishing) and creates
535
+ a GitHub release with generated notes.
536
+
537
+ Use the `just release` recipe to cut a release. It runs in the safe order - tests
538
+ first, then version bump and tag push:
533
539
 
534
540
  ```bash
535
- # Update version in pyproject.toml manually
536
- # Or use a tool like bump2version:
537
- # bump2version patch # for bug fixes
538
- # bump2version minor # for new features
539
- # bump2version major # for breaking changes
541
+ just release # next patch version (default)
542
+ just release dev # next dev prerelease
543
+ just release minor # next minor version
544
+ just release major # next major version
540
545
  ```
541
546
 
547
+ This runs `uv run pytest`, then `uv version --bump <bump>`, commits the change,
548
+ creates an annotated `vX.Y.Z` tag and pushes it with `git push --follow-tags`.
549
+ Accepted bump values: `major`, `minor`, `patch`, `stable`, `alpha`, `beta`, `rc`,
550
+ `post`, `dev`.
551
+
542
552
  ### Changelog
543
553
 
544
554
  Update the changelog for releases:
@@ -564,13 +574,14 @@ Update the changelog for releases:
564
574
 
565
575
  ### Pre-release Checklist
566
576
 
567
- Before releasing:
577
+ Before running `just release`:
568
578
 
569
- - [ ] All tests passing
570
579
  - [ ] Documentation updated
571
- - [ ] Version bumped appropriately
572
580
  - [ ] Changelog updated
573
- - [ ] No breaking changes without major version bump
581
+ - [ ] Correct bump chosen (no breaking changes without a major version bump)
582
+ - [ ] On the branch you intend to release from
583
+
584
+ `just release` then runs the tests, bumps the version and pushes the tag for you.
574
585
 
575
586
  ## Getting Help
576
587
 
@@ -447,26 +447,47 @@ Use the configured tasks for development:
447
447
 
448
448
  ## Release Process
449
449
 
450
- ### Version Management
451
- ```bash
452
- # Update version in pyproject.toml using either
453
- uv version <new_version>
454
- uv version --bump patch
450
+ Releases are automated through GitHub Actions. Pushing a `v*` tag triggers
451
+ [`.github/workflows/release.yml`](../.github/workflows/release.yml), which:
452
+
453
+ 1. Builds the sdist and wheel with `uv build`
454
+ 2. Publishes them to PyPI using **trusted publishing** (OIDC, no token required)
455
+ 3. Creates a GitHub release with auto-generated notes
456
+
457
+ ### Cutting a release
458
+
459
+ The `just release` recipe drives the whole local flow in the safe order - it runs
460
+ the tests first, and only then bumps the version and pushes the tag:
455
461
 
456
- # Recommended: update the included boards & description information
462
+ ```bash
463
+ # 1. (optional) refresh the bundled boards & descriptions before releasing
457
464
  uv run mpflash/db/gather_boards.py --mpy-path ../micropython
458
465
 
459
- # Build package
460
- uv build
466
+ # 2. run tests, bump the version, commit, tag and push (triggers the PyPI release)
467
+ just release # next patch version (default)
468
+ just release dev # next dev prerelease
469
+ just release minor # next minor version
470
+ ```
461
471
 
462
- # Publish to PyPI
463
- # set token in environment variable
472
+ `just release` performs, in order:
464
473
 
465
- ```pwsh
466
- uv install keyring
467
- $env:UV_PUBLISH_TOKEN=(python -m keyring get pypi uv_publish
468
- del env:UV_PUBLISH_TOKEN
474
+ 1. `uv run pytest` - abort the release if any test fails
475
+ 2. `uv version --bump <bump>` - update `pyproject.toml` and re-lock
476
+ 3. `git commit` the version change
477
+ 4. `git tag -a vX.Y.Z` and `git push --follow-tags`
478
+
479
+ The accepted bump values are `major`, `minor`, `patch` (default), `stable`,
480
+ `alpha`, `beta`, `rc`, `post` and `dev`.
481
+
482
+ ### Manual fallback
483
+
484
+ Only if the GitHub Actions publish is unavailable:
485
+
486
+ ```bash
487
+ just build # or: uv build
488
+ just publish # or: uv publish (requires PyPI credentials)
469
489
  ```
490
+
470
491
  ### Documentation Updates
471
492
  - Update README.md with new features
472
493
  - Add changelog entries
@@ -1,24 +1,47 @@
1
- ## Linux permissions for usb devices
1
+ ## Linux permissions for USB devices
2
2
 
3
- In order to flash the firmware to the board, you need to have the correct permissions to access the USB devices.
4
- The details will depend on the specific USB device and the operating system you are using.
3
+ In order to flash STM32 boards in DFU mode without `sudo`, your user needs
4
+ permission on the DFU USB device node.
5
5
 
6
- You can use the following udev rules to give non-root users access to the USB devices.
6
+ Ready-to-use rule file in this repository:
7
+
8
+ - `mpflash/udev_rules/65-mpflash-stm32-dfu.rules`
9
+
10
+ ### Install (copy/paste)
7
11
 
8
- File: `/etc/udev/rules.d/65-mpflash.rules`
9
12
  ```bash
10
- # allow non-root users to access to stm32 device in dfu mode (bootloader)
11
- SUBSYSTEM=="usb", ACTION=="add", ATTR{product}=="STM32 BOOTLOADER", GROUP="plugdev", MODE="0660"
13
+ sudo cp mpflash/udev_rules/65-mpflash-stm32-dfu.rules /etc/udev/rules.d/65-mpflash-stm32-dfu.rules
14
+ sudo udevadm control --reload-rules
15
+ sudo udevadm trigger
12
16
  ```
13
- reload the udev rules with the following command:
17
+
18
+ Then unplug/replug the STM32 board (or re-enter DFU mode).
19
+
20
+ ### Rule contents
21
+
22
+ ```udev
23
+ # mpflash STM32 DFU access rule
24
+ # Device: STMicroelectronics STM Device in DFU Mode (0483:df11)
25
+ SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="df11", MODE="0660", GROUP="plugdev", TAG+="uaccess"
26
+ ```
27
+
28
+ ### Verify
29
+
30
+ ```bash
31
+ lsusb | grep -i "0483:df11"
32
+ groups
33
+ ```
34
+
35
+ If your user is not in `plugdev`, add it and log out/in once:
36
+
14
37
  ```bash
15
- sudo udevadm control --reload
38
+ sudo usermod -aG plugdev "$USER"
16
39
  ```
17
- Unplug and replug the usb device to apply the new rules.
18
40
 
19
41
 
20
- ## to check
21
- Enter the stm32 bootloader mode
42
+ ## To check
43
+
44
+ Enter STM32 bootloader mode:
22
45
  ``` bash
23
46
  mpremote bootloader
24
47
  ```
@@ -49,14 +72,14 @@ List usb devices
49
72
  ID 8087:0aaa Intel Corp. Bluetooth 9460/9560 Jefferson Peak (JfP)
50
73
  /sys/bus/usb/devices/1-10 /dev/bus/usb/001/003
51
74
  ```
52
- Lookup the stm32 device device path ( /dev/bus/usb/001/022),
53
- and check if the group `plugdev` is granted access using `ll`
75
+ Lookup the STM32 device path (`/dev/bus/usb/001/022`), and check if group
76
+ `plugdev` is granted access:
54
77
  ```bash
55
78
  (.venv) jos@jvnuc:~/projects/mpflash$ ll /dev/bus/usb/001/022
56
79
  crw-rw-r-- 1 root plugdev 189, 21 mrt 11 22:38 /dev/bus/usb/001/022
57
80
  ```
58
81
 
59
- Check `groups` to see if user is in plugdev group
82
+ Check `groups` to see if user is in `plugdev`:
60
83
  ```
61
84
  (.venv) jos@jvnuc:~/projects/mpflash$ groups
62
85
  jos adm disk dialout cdrom sudo dip plugdev kvm lpadmin lxd sambashare usb
@@ -0,0 +1,87 @@
1
+
2
+ # Set shell for Windows OSs:
3
+ set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"]
4
+
5
+ # Show recipes
6
+ help:
7
+ @just --list
8
+
9
+ # install project in edi mode and install dev dependencies
10
+ sync:
11
+ uv sync --extra dev,test,pyocd
12
+
13
+ init:
14
+ @just sync
15
+
16
+ # run full pytest suite
17
+ test:
18
+ uv run pytest -v
19
+
20
+ # create coverage report, build html report
21
+ coverage:
22
+ uv run pytest --cov --cov-report=term-missing
23
+ coverage html
24
+ start coverage/index.html
25
+
26
+ # bump mpflash's version [major, minor, patch, stable, alpha, beta, rc, post, dev]
27
+ bump bump="patch":
28
+ uv version --bump {{bump}}
29
+
30
+ # build the project for distribution
31
+ build:
32
+ uv build
33
+
34
+ # -----------------------------------------------------------------------------------------------
35
+ # Release process
36
+ # Pushing a `v*` tag triggers .github/workflows/release.yml, which builds the sdist + wheel,
37
+ # publishes to PyPI using trusted publishing (OIDC, no token) and creates a GitHub release
38
+ # with auto-generated notes. Use `just release` to cut a release; do not publish manually.
39
+ # -----------------------------------------------------------------------------------------------
40
+
41
+ # run tests, bump the version, then commit, tag and push - triggering the PyPI release
42
+ # bump = major | minor | patch (default) | stable | alpha | beta | rc | post | dev
43
+ [confirm("Run tests, bump the version and push a new tag? This triggers a PyPI release. Continue?")]
44
+ release bump="patch":
45
+ uv run pytest
46
+ uv version --bump {{bump}}
47
+ git add pyproject.toml uv.lock
48
+ git commit -m "Release v$(uv version --short)"
49
+ git tag -a "v$(uv version --short)" -m "Release v$(uv version --short)"
50
+ git push --follow-tags
51
+
52
+ # manual fallback: publish to PyPI directly (normally handled by GitHub Actions on tag push)
53
+ [confirm("Are you sure you want to publish to PyPI directly? This is normally handled by GitHub Actions on tag push. Continue?")]
54
+ publish : build
55
+ uv publish
56
+
57
+ # delete and regen the lockfile - useful in merge conficts
58
+ lock:
59
+ del uv.lock -erroraction ignore
60
+ uv lock
61
+
62
+ # -----------------------------------------------------------------------------------------------
63
+ # HIL testing
64
+ # Configure environment for the port and firmware selection in the .env file, then run the test suite
65
+ # -----------------------------------------------------------------------------------------------
66
+ # export LOGURU_LEVEL := "TRACE"
67
+ export LOGURU_LEVEL := "INFO"
68
+
69
+ hil_uf2_pico2:
70
+ uv run pytest -m hw_uf2 tests/hw -v
71
+
72
+ hil_dfu_pybv11:
73
+ uv run pytest -m hw_dfu tests/hw -v
74
+
75
+ hil_pyocd_pybv11:
76
+ uv run pytest -m hw_pyocd tests/hw -v
77
+
78
+ hil_esptool_esp32:
79
+ uv run pytest -m hw_esptool tests/hw -v
80
+
81
+ # [script('python')]
82
+ # python:
83
+ # print('Hello from python!')
84
+ # from pathlib import Path
85
+ # print(f'Current directory: {Path.cwd()}')
86
+
87
+
@@ -0,0 +1,118 @@
1
+ """Drive a board into bootloader mode using pluggable activators.
2
+
3
+ The orchestration walks the ordered list of activators returned by
4
+ :func:`mpflash.bootloader.registry.resolve_methods`, calling each in turn
5
+ and confirming with :func:`mpflash.bootloader.detect.in_bootloader` (which
6
+ delegates to the flash backend's ``is_board_ready`` when one is supplied).
7
+
8
+ Built-in activators register themselves on import; flash plugins can
9
+ contribute additional activators by calling
10
+ :func:`mpflash.bootloader.registry.register`.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import time
16
+ from typing import Optional
17
+
18
+ from mpflash.bootloader.detect import backend_for_port, in_bootloader
19
+ from mpflash.bootloader.registry import get_activator, resolve_methods
20
+ from mpflash.common import BootloaderMethod
21
+ from mpflash.errors import MPFlashError
22
+ from mpflash.logger import log
23
+ from mpflash.mpremoteboard import MPRemoteBoard
24
+ from mpflash.flash.base import FlashBackend
25
+
26
+
27
+ def enter_bootloader(
28
+ mcu: MPRemoteBoard,
29
+ method: BootloaderMethod = BootloaderMethod.MPY,
30
+ timeout: int = 10,
31
+ wait_after: int = 2,
32
+ *,
33
+ backend: Optional[FlashBackend] = None,
34
+ ) -> bool:
35
+ """Put the board into bootloader mode.
36
+
37
+ Args:
38
+ mcu: The target board.
39
+ method: Which BootloaderMethod to try first. AUTO walks the
40
+ backend's preferred order. NONE skips activation entirely.
41
+ timeout: Per-attempt timeout in seconds.
42
+ wait_after: Seconds to wait between an activator call and the
43
+ readiness check.
44
+ backend: Optional flash backend used to look up the preferred
45
+ activator order and to confirm readiness via
46
+ ``backend.is_board_ready``. When omitted, the flash registry is
47
+ consulted for a backend matching ``mcu.port``.
48
+
49
+ Returns:
50
+ True when the board is reported in bootloader mode, False if every
51
+ attempted activator failed.
52
+ """
53
+ if method == BootloaderMethod.NONE:
54
+ # No bootloader requested — assume it is OK to flash.
55
+ return True
56
+
57
+ if backend is None:
58
+ backend = backend_for_port(mcu.port)
59
+ preferred = list(backend.get_preferred_bootloaders(mcu)) if backend else []
60
+ method_list = resolve_methods(method.value, preferred)
61
+ if not method_list:
62
+ log.debug(
63
+ f"No bootloader activators resolved for {mcu.port} (method={method.value})"
64
+ )
65
+ return True
66
+
67
+ log.info(
68
+ f"Entering bootloader on {mcu.serialport} using methods {method_list}"
69
+ )
70
+
71
+ result = False
72
+ for name in method_list:
73
+ activator = get_activator(name)
74
+ if activator is None:
75
+ log.debug(f"Unknown bootloader activator {name!r}; skipping")
76
+ continue
77
+ try:
78
+ result = bool(activator.activate(mcu, timeout=timeout))
79
+ except MPFlashError as e:
80
+ log.warning(
81
+ f"Failed to enter bootloader on {mcu.serialport} using {name}"
82
+ )
83
+ log.exception(e)
84
+ result = False
85
+ if not result:
86
+ continue
87
+
88
+ # todo - check every second or so for up to max wait time
89
+ time.sleep(wait_after)
90
+ if in_bootloader(mcu, backend=backend):
91
+ return True
92
+
93
+ serialport = mcu.serialport
94
+ if serialport and serialport.lower() != "auto":
95
+ try:
96
+ connected = {port.casefold() for port in MPRemoteBoard.connected_comports()}
97
+ except OSError:
98
+ connected = {serialport.casefold()}
99
+ if serialport.casefold() not in connected:
100
+ # The serial port vanished, so the board reset into its UF2
101
+ # bootloader. Do not fall through to touch-1200 (which would
102
+ # try to open a port that no longer exists). Give the UF2
103
+ # volume extra time to mount and confirm it is actually there
104
+ # before reporting success — otherwise the flash backend will
105
+ # abort with "Board is not in bootloader mode".
106
+ log.debug(
107
+ f"Serial port {serialport} disappeared after {name}; "
108
+ "waiting for the UF2 drive to mount"
109
+ )
110
+ if in_bootloader(mcu, backend=backend):
111
+ return True
112
+ log.warning(
113
+ f"{serialport} reset into bootloader but the UF2 drive "
114
+ "was not detected"
115
+ )
116
+ continue
117
+
118
+ return False
@@ -0,0 +1,55 @@
1
+ """Abstract base class for pluggable bootloader activators.
2
+
3
+ An *activator* knows one way to put a board into firmware-update / bootloader
4
+ mode (e.g. send ``machine.bootloader()``, open the port at 1200 baud, or ask
5
+ the user to press a button). Built-ins live in
6
+ ``mpflash.bootloader.builtins`` and register themselves at import time;
7
+ third-party flash plugins can call :func:`mpflash.bootloader.registry.register`
8
+ to contribute additional activators.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from abc import ABC, abstractmethod
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ from mpflash.mpremoteboard import MPRemoteBoard
18
+
19
+
20
+ class BootloaderActivator(ABC):
21
+ """Contract every bootloader activator must implement.
22
+
23
+ Subclasses are typically singletons; the registry instantiates each
24
+ activator once. The ``name`` attribute must match the corresponding
25
+ :class:`mpflash.common.BootloaderMethod` enum *value* for the built-in
26
+ activators (``"mpy"``, ``"touch1200"``, ``"manual"``); plugin-supplied
27
+ activators may use any unique string.
28
+ """
29
+
30
+ #: Short identifier (matches ``BootloaderMethod.value`` for built-ins).
31
+ name: str = ""
32
+
33
+ def is_available(self, mcu: "MPRemoteBoard") -> bool:
34
+ """Runtime availability check for this MCU.
35
+
36
+ Default returns ``True``. Activators with environmental
37
+ prerequisites (USB drivers, GUI prompts on a headless host, …)
38
+ should override.
39
+ """
40
+ return True
41
+
42
+ @abstractmethod
43
+ def activate(self, mcu: "MPRemoteBoard", *, timeout: int = 10) -> bool:
44
+ """Attempt to put ``mcu`` into bootloader mode.
45
+
46
+ Returns ``True`` if the activator believes the attempt succeeded;
47
+ the caller is responsible for confirming readiness afterwards.
48
+ """
49
+ raise NotImplementedError
50
+
51
+ def __str__(self) -> str:
52
+ return f"{self.__class__.__name__}({self.name})"
53
+
54
+ def __repr__(self) -> str:
55
+ return f"<{self.__class__.__name__} name={self.name!r}>"