micropython-stubber 1.20.1__py3-none-any.whl → 1.20.2__py3-none-any.whl

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 (58) hide show
  1. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/METADATA +3 -3
  2. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/RECORD +56 -49
  3. mpflash/README.md +16 -5
  4. mpflash/mpflash/add_firmware.py +98 -0
  5. mpflash/mpflash/ask_input.py +97 -120
  6. mpflash/mpflash/cli_download.py +42 -25
  7. mpflash/mpflash/cli_flash.py +70 -32
  8. mpflash/mpflash/cli_group.py +14 -12
  9. mpflash/mpflash/cli_list.py +39 -3
  10. mpflash/mpflash/cli_main.py +17 -6
  11. mpflash/mpflash/common.py +125 -12
  12. mpflash/mpflash/config.py +2 -0
  13. mpflash/mpflash/connected.py +74 -0
  14. mpflash/mpflash/download.py +56 -42
  15. mpflash/mpflash/downloaded.py +9 -9
  16. mpflash/mpflash/flash.py +2 -2
  17. mpflash/mpflash/flash_esp.py +2 -2
  18. mpflash/mpflash/flash_uf2.py +16 -8
  19. mpflash/mpflash/flash_uf2_linux.py +5 -16
  20. mpflash/mpflash/flash_uf2_macos.py +78 -0
  21. mpflash/mpflash/flash_uf2_windows.py +1 -1
  22. mpflash/mpflash/list.py +57 -57
  23. mpflash/mpflash/mpboard_id/__init__.py +37 -44
  24. mpflash/mpflash/mpboard_id/add_boards.py +255 -0
  25. mpflash/mpflash/mpboard_id/board.py +37 -0
  26. mpflash/mpflash/mpboard_id/board_id.py +38 -34
  27. mpflash/mpflash/mpboard_id/board_info.zip +0 -0
  28. mpflash/mpflash/mpboard_id/store.py +42 -0
  29. mpflash/mpflash/mpremoteboard/__init__.py +18 -6
  30. mpflash/mpflash/uf2disk.py +12 -0
  31. mpflash/mpflash/vendor/basicgit.py +288 -0
  32. mpflash/mpflash/vendor/dfu.py +1 -0
  33. mpflash/mpflash/vendor/versions.py +7 -3
  34. mpflash/mpflash/worklist.py +71 -48
  35. mpflash/poetry.lock +164 -138
  36. mpflash/pyproject.toml +18 -15
  37. stubber/__init__.py +1 -1
  38. stubber/board/createstubs.py +4 -3
  39. stubber/board/createstubs_db.py +5 -7
  40. stubber/board/createstubs_db_min.py +329 -825
  41. stubber/board/createstubs_db_mpy.mpy +0 -0
  42. stubber/board/createstubs_mem.py +6 -7
  43. stubber/board/createstubs_mem_min.py +304 -765
  44. stubber/board/createstubs_mem_mpy.mpy +0 -0
  45. stubber/board/createstubs_min.py +293 -975
  46. stubber/board/createstubs_mpy.mpy +0 -0
  47. stubber/board/modulelist.txt +1 -0
  48. stubber/commands/get_core_cmd.py +7 -6
  49. stubber/commands/get_docstubs_cmd.py +8 -3
  50. stubber/commands/get_frozen_cmd.py +5 -2
  51. stubber/publish/publish.py +18 -7
  52. stubber/utils/makeversionhdr.py +3 -2
  53. stubber/utils/versions.py +2 -1
  54. mpflash/mpflash/mpboard_id/board_info.csv +0 -2213
  55. mpflash/mpflash/mpboard_id/board_info.json +0 -19910
  56. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/LICENSE +0 -0
  57. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/WHEEL +0 -0
  58. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.2.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: micropython-stubber
3
- Version: 1.20.1
3
+ Version: 1.20.2
4
4
  Summary: Tooling to create and maintain stubs for MicroPython
5
5
  Home-page: https://github.com/Josverl/micropython-stubber#readme
6
6
  License: MIT
@@ -129,8 +129,8 @@ cd my_stub_folder
129
129
  mkdir all-stubs
130
130
 
131
131
  # clone the micropython repo's and switch to a specific version
132
- stubber clone
133
- stubber switch --version v1.18
132
+ stubber clone --add-stubs
133
+ stubber switch v1.22.2
134
134
 
135
135
  # get the document stubs for the current version ( v1.18 )
136
136
  stubber get-docstubs
@@ -1,63 +1,70 @@
1
1
  mpflash/libusb_flash.ipynb,sha256=7RGtGg1mPPMWrfJOobOfYYwAAhsFpykpTJtfNwevY9o,6631
2
2
  mpflash/mpflash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- mpflash/mpflash/ask_input.py,sha256=_LlatVyrWbY1Q4TDn8TWXJO5s22q_bGl3NXmdt13jxU,8442
4
- mpflash/mpflash/cli_download.py,sha256=RNEsk1eMqzKCf9_sUPZQkDnzw6w-8L0Jnl6gDQup2Tc,3266
5
- mpflash/mpflash/cli_flash.py,sha256=DTZ1mQSL31aEr3ROGIGWzzkiir4MRSqXdwq2p4ArDWE,5847
6
- mpflash/mpflash/cli_group.py,sha256=nL3H06PHm_XUDlMuRyjgmTYeLnkrLa9mKDdahYw-KRo,1967
7
- mpflash/mpflash/cli_list.py,sha256=KIlEeqcIIBf0g-emS43fzKspUy6fn9TUuFl0u00XaK8,1024
8
- mpflash/mpflash/cli_main.py,sha256=BgqkDeEV0LBdT_Xn_Ay3zQOVJ-73pWSA4ngRf9KxGpw,656
9
- mpflash/mpflash/common.py,sha256=lucFGMLl03qz-5Ic2XVv4g5XVt6hloUU6N5v0tSaUYE,1049
10
- mpflash/mpflash/config.py,sha256=G6TxliEGxoYXy1SHQYBKgywnKccz9QzD3mGq_Vv1frg,419
11
- mpflash/mpflash/download.py,sha256=_yYENI7oew4tD51xEer1Ohv2B3LHrGg1lIF98LWDMCY,10991
12
- mpflash/mpflash/downloaded.py,sha256=ADMJqZn7WVcU-Rm2X6RqA8ejtBNBYXcpwxVyT3v7r6s,3803
3
+ mpflash/mpflash/add_firmware.py,sha256=u_g9mID557fptLEJ1Nld9n27V1R1og8uEkadm0O3YTw,3435
4
+ mpflash/mpflash/ask_input.py,sha256=e54WfgktG7Ff8dBUknXioROq3lPNZ-eacUjfmUvFfS0,8496
5
+ mpflash/mpflash/cli_download.py,sha256=6ctC4ga6q2LcpHUHMrz1trbePziQpuIFPbjcJnc3K7E,3645
6
+ mpflash/mpflash/cli_flash.py,sha256=QHEb-e2ABjISfuGCWpTxOM7kkcTZSQxBXmVaWh3WVnE,7074
7
+ mpflash/mpflash/cli_group.py,sha256=-qkR4ou7bmIwfDIzyL1s34z6ya1wckKtL22nanvyQGU,1974
8
+ mpflash/mpflash/cli_list.py,sha256=Mdaf13gKZCoLp8Y2ja0L5rYMzkE_t3d4r62bF7isI3E,1997
9
+ mpflash/mpflash/cli_main.py,sha256=yABFFf45TpPMcC1qEVARAWe4EI9zui2pUXjoPms0mq8,1030
10
+ mpflash/mpflash/common.py,sha256=kanbO22SQDWb4ASQG8vLF0Z83ahujKPbrsCw3llN5T4,5594
11
+ mpflash/mpflash/config.py,sha256=hBc1Hf4XQ0YIoeiPBbannnqDg0C7226Y7Vu8o4jUkBQ,495
12
+ mpflash/mpflash/connected.py,sha256=CBG_DJ33OPWAPbX-ICQpL1LcFOhNYpLUSB0Q5v7gi9s,3029
13
+ mpflash/mpflash/download.py,sha256=axAs30i7VZ0jQXY9MVOQB49JH6OCFxy5_zERfysF33I,11421
14
+ mpflash/mpflash/downloaded.py,sha256=IYMyx_67paKFSKOg3whgwsWf8IlHaLgpmSlukMT7V9Q,3804
13
15
  mpflash/mpflash/errors.py,sha256=Q5LR12Wo8iUCg5n_qq4GjdBdBflbvCOdKsRJ5InYRfI,96
14
- mpflash/mpflash/flash.py,sha256=YGYXuNNbjro4QvZmpwpLCo86nFsh4UxWrOJHOowUYDY,2490
15
- mpflash/mpflash/flash_esp.py,sha256=TjBOk2y1eLrcE8T3iYGypsiskPX7BFNfxYmCuUo_3v4,2316
16
+ mpflash/mpflash/flash.py,sha256=JoskuwaHVYqeG4YW8kgbv26vPFnqDmkTz1VRs-pTRiY,2468
17
+ mpflash/mpflash/flash_esp.py,sha256=dEc_B7-f1BMUGBMrwIm83ulcCqaS5MlrPAp3FCNgNfk,2311
16
18
  mpflash/mpflash/flash_stm32.py,sha256=d4BoQl3a9Tchnvn2ZTuq2MpYBB4MTaRukwtEncI95k0,823
17
19
  mpflash/mpflash/flash_stm32_cube.py,sha256=w7aGWjReeWUKl0Q3ZjXH8BRqNO1Tk9AO7gtRNUg1c9Y,3970
18
20
  mpflash/mpflash/flash_stm32_dfu.py,sha256=G70EZodWb-aRi507Jxbys-VEwbBGU1oZacow3_nq-d4,2972
19
- mpflash/mpflash/flash_uf2.py,sha256=KvNPk1zDwQexJfPI5MlIoR7zTD0u-pQQwSHuFQjuMXg,2093
21
+ mpflash/mpflash/flash_uf2.py,sha256=FFO2zpKFPdpEDi4aK1-JFsvPi8KEKLXXuOt_lXtI9tk,2508
20
22
  mpflash/mpflash/flash_uf2_boardid.py,sha256=WZKucGu_hJ8ymb236uuZbiR6pD6AA_l4LA-7LwtQhq8,414
21
- mpflash/mpflash/flash_uf2_linux.py,sha256=LAGkzTImVq-wKo7LGUNlwkUHv1L4rGO7igR5dwxY07o,4298
22
- mpflash/mpflash/flash_uf2_windows.py,sha256=dcmA-koavH7duOuNwI0n2aDDbhF1_5ZZ-mXFAXgj8z4,1072
23
- mpflash/mpflash/list.py,sha256=R3upYux3mEltpqfrt467Ufs4hVatW1NE40jjhN7Ei1g,3252
23
+ mpflash/mpflash/flash_uf2_linux.py,sha256=Oy9V4g7JSuks2hHFeO_OHdBKSGktbqZOtsivuxfl-xg,4055
24
+ mpflash/mpflash/flash_uf2_macos.py,sha256=JButLjccuKlVyArwNkHDGjNMB_9ezyTKlbCWNia-R5Y,2696
25
+ mpflash/mpflash/flash_uf2_windows.py,sha256=94YoO2UIzfyJs4CPJ9sjG_WY26SX8aUPl9mf9R9W5xk,1093
26
+ mpflash/mpflash/list.py,sha256=0TawTkwhjKPPj7nTHoDn8nQ54WOkGRurP1BJVeg956g,2963
24
27
  mpflash/mpflash/logger.py,sha256=dI_H_a7EOdQJyvoeRHQuYeZuTKYVUS3DUPTLhE9rkdM,1098
25
- mpflash/mpflash/mpboard_id/__init__.py,sha256=JYGe7VwpBV4ig2M9a6vJUQrMtgdNjZKHt_Z5N13Ycrs,3509
26
- mpflash/mpflash/mpboard_id/board_id.py,sha256=G2oW969pqDuLbQS7UrFNmbWNdDIXfWBAoZMt1EWHLak,2361
27
- mpflash/mpflash/mpboard_id/board_info.csv,sha256=KPWDo-zHWfrPGQn9oInsDH-5IdCzhBCs6K_YAmqqSpQ,96983
28
- mpflash/mpflash/mpboard_id/board_info.json,sha256=JtVyOMIO1O7vLKzJ0hyXQ4JSxXiQBJyay2hjdNLnZM0,674442
29
- mpflash/mpflash/mpremoteboard/__init__.py,sha256=DxlO_7LiyWDz5hNRI77fzp3sI3fZQ9Sd23dnGLx4Zl0,7017
28
+ mpflash/mpflash/mpboard_id/__init__.py,sha256=QRLVUpeiXeiY3P-im2Bv8zX8xGS_MIqiH-3svuVoWyY,3576
29
+ mpflash/mpflash/mpboard_id/add_boards.py,sha256=OWclyLWf9L-pCVmZ22b-xQYfvi3yQVsJHmGMgMzWxoU,9684
30
+ mpflash/mpflash/mpboard_id/board.py,sha256=CwtBux8y7GDUe7CADVxL8YefGRl9Fg8OAJBUhgaBYCI,1151
31
+ mpflash/mpflash/mpboard_id/board_id.py,sha256=63K91S_KWdSL5vHkZhpU-ibOhpOrUt0YPwxDsUBEmnc,2489
32
+ mpflash/mpflash/mpboard_id/board_info.zip,sha256=F6YowS96DAqjten4ySe4MXgZwPtE-saZOUfY5OQkqKk,19759
33
+ mpflash/mpflash/mpboard_id/store.py,sha256=hhNTPlraQYH_Tx3syoi_NBx8z13efkgXn433OZ2GjpU,1462
34
+ mpflash/mpflash/mpremoteboard/__init__.py,sha256=fJ_N1F6R3CfP9F7pmocb5l8yRvzmSmtHi4u_uTQHR1w,7683
30
35
  mpflash/mpflash/mpremoteboard/mpy_fw_info.py,sha256=6AQbN3jtQgllqWQYl4e-63KeEtV08EXk8_JnM6XBkvo,4554
31
36
  mpflash/mpflash/mpremoteboard/runner.py,sha256=-PgzAeBGbyXaAUlwyiw4mcINsP2U1XRRjP1_QdBrxpg,4786
32
- mpflash/mpflash/vendor/dfu.py,sha256=oK_MRSOyDJrUuS6D24IMIsfL7oLcrvUq0yp_h4WIY2U,5739
37
+ mpflash/mpflash/uf2disk.py,sha256=4_P2l-kedM7VSliA2u706LQLxvu3xWSod1-lj-xjZis,298
38
+ mpflash/mpflash/vendor/basicgit.py,sha256=sflgCv7apLbV2w8F6gmhc-3kuqDnnS4tdGol6JT2uTM,9545
39
+ mpflash/mpflash/vendor/dfu.py,sha256=ZXMcE6aH4-43Wh4tbQT4U-q-BU3RUiL3JAxmP_QAK2s,5755
33
40
  mpflash/mpflash/vendor/pydfu.py,sha256=_MdBRo1EeNeKDqFPSTB5tNL1jGSBJgsVeVjE5e7Pb8s,20542
34
41
  mpflash/mpflash/vendor/readme.md,sha256=iIIZxuLUIGHQ0KODzYVtMezsztvyxCXcNJp_AzwTIPk,86
35
- mpflash/mpflash/vendor/versions.py,sha256=ooRZjeeYepQHwp12hMu2m0p8nZXQ5s942w5mGkKmgeI,3629
36
- mpflash/mpflash/worklist.py,sha256=qZsqF3Lf5Bl7QQ31ZLVHewP6WC8fmwQPMbyNgbG7LB4,5299
37
- mpflash/poetry.lock,sha256=9dYWM12d3iPBuZ6Mqf2qRRRM6eECbonLY6xFJKokrpE,120389
38
- mpflash/pyproject.toml,sha256=nsRtomDdZGoYDejdi17PSz6mrEVxygGEj98bZJuxYDw,1630
39
- mpflash/README.md,sha256=uxCY6m2gIWsdruD78ftesQzGdfdpCWbkZF4yQuVyvio,13159
42
+ mpflash/mpflash/vendor/versions.py,sha256=thk1a5wEEhXIQoL0zZ7oooeFyQeSoI00CIUbZF0b3rI,3783
43
+ mpflash/mpflash/worklist.py,sha256=MKHDynttVP3lsHSfb7DEqeQ2mRV0da96lD4lIcS_zQY,5962
44
+ mpflash/poetry.lock,sha256=pHC8OhQvF5HWKPlw2Ysd39cL4XmJIqDX_Lwo-ngm1SY,121346
45
+ mpflash/pyproject.toml,sha256=xaMDME1wBJf-KTf_qqK2jlY7f3o3b1L8utaXw8ZK8PU,1651
46
+ mpflash/README.md,sha256=1SVCxNG88akXYv1BOeLvqwfI9phy8PdkfJ2sqxL_nkE,13680
40
47
  mpflash/stm32_udev_rules.md,sha256=uxvC8FvU7K0R1QQUqCIvVfW9yfWYlIHhIVtirAjQVHE,2684
41
- stubber/__init__.py,sha256=z5jowDKZq7kcJXHn0znrH5iWaaETNkTGF_MiwaaJko0,49
48
+ stubber/__init__.py,sha256=tthm_uidIn3vd1lcFmiTkjY46W8_vGp6D9i0K_FJuds,49
42
49
  stubber/basicgit.py,sha256=sflgCv7apLbV2w8F6gmhc-3kuqDnnS4tdGol6JT2uTM,9545
43
50
  stubber/board/board_info.csv,sha256=K2VSmfR013fN-oJWkQUmiQ19w09dVwJHDquPy6QmMhY,8627
44
51
  stubber/board/boot.py,sha256=XjWlKErU5nI1HJSugXIP_3hlwgRQboE6sJrpcbSygnk,1120
45
- stubber/board/createstubs.py,sha256=W3h-okYdGkHxus6aPBHV57DW0C-8K83GF0NjdFzPry8,32723
46
- stubber/board/createstubs_db.py,sha256=U2PN4UDri5-zRfadKELaIh7NP73VB4ncw8NHKjkDoE4,30445
47
- stubber/board/createstubs_db_min.py,sha256=U2PN4UDri5-zRfadKELaIh7NP73VB4ncw8NHKjkDoE4,30445
48
- stubber/board/createstubs_db_mpy.mpy,sha256=CdE7teLLvyKAd4ptwKSEKSQ4A35nPN5_ul8sJqu0tH8,9238
52
+ stubber/board/createstubs.py,sha256=eA1mHRmjhVENwtRjdcqmfC-x_U9peimZJETTBbaMagE,32739
53
+ stubber/board/createstubs_db.py,sha256=xZqBLLfdrMYdG_sW_ZzvoOEFTvPcT5R5S3zN1pWhtxo,30419
54
+ stubber/board/createstubs_db_min.py,sha256=J0HwWl5xsTcnq-K9hDU4V5aAxfOJqHHvNZTI-wxmLOs,11473
55
+ stubber/board/createstubs_db_mpy.mpy,sha256=KJeOJW1y_PKI9hrtJaUyy07Y_DiV57rT71bolf13L-E,9536
49
56
  stubber/board/createstubs_lvgl.py,sha256=CTe7eq1ACRK_JJxavaqDD8znn29nSWJiHHTZ_ps6EhM,27217
50
57
  stubber/board/createstubs_lvgl_min.py,sha256=jLkWYmeboI2A8feMC7pT7cYWttLejQTuX7WAEZCylhw,27207
51
58
  stubber/board/createstubs_lvgl_mpy.mpy,sha256=ex-nlq2V5e8anQBJvRWEEc-FzU7nlwg5NSrZ8vOadIA,9267
52
- stubber/board/createstubs_mem.py,sha256=y_h7EvgmCmAxo1a_PmaXe9nV3YjmCPtHfGl7kriYaFw,28779
53
- stubber/board/createstubs_mem_min.py,sha256=y_h7EvgmCmAxo1a_PmaXe9nV3YjmCPtHfGl7kriYaFw,28779
54
- stubber/board/createstubs_mem_mpy.mpy,sha256=yo4ePyPsY2_tVPCz1Su-zqkullidt-seIbYDzYpBPzQ,8851
55
- stubber/board/createstubs_min.py,sha256=W3h-okYdGkHxus6aPBHV57DW0C-8K83GF0NjdFzPry8,32723
56
- stubber/board/createstubs_mpy.mpy,sha256=PxMv6ARUjMLYOp2K47rLFIztuJJ8-FHyw9rPnEKCFzo,12076
59
+ stubber/board/createstubs_mem.py,sha256=_SRTvJG78fbEDCFii82Cof6cJwF_xqkXeOFDIJAx9o8,28755
60
+ stubber/board/createstubs_mem_min.py,sha256=8tTzYjlgepjhepMLplN_8eOGLjEKY2NpffGPvGelP-I,11019
61
+ stubber/board/createstubs_mem_mpy.mpy,sha256=RVuULJrwFQT0zJqa3JYrnOdbbU51K7rDX8mKR6Elq3k,9118
62
+ stubber/board/createstubs_min.py,sha256=1x0SpetEPuH8rnRkF9mE3qltevkKlA4tE9DySXkmwFM,13570
63
+ stubber/board/createstubs_mpy.mpy,sha256=spYC-x1c2zOOzVz2n-2roqFCcMNNFwsDjUsKPFuCuKk,12236
57
64
  stubber/board/fw_info.py,sha256=6AQbN3jtQgllqWQYl4e-63KeEtV08EXk8_JnM6XBkvo,4554
58
65
  stubber/board/info.py,sha256=b7SOPZHVsVhaayKCwVkFZlYu0BW-UFI7LuG1Eop9480,5629
59
66
  stubber/board/main.py,sha256=f6V3tdt6sPZVLuwemT-NLuK9GySfW2c2J6PJMOOWQQw,413
60
- stubber/board/modulelist.txt,sha256=LrFH2H7tHZCA960yIa5-6mHItwI5N2LwQZ_IveDoyYs,2775
67
+ stubber/board/modulelist.txt,sha256=DUD4wbJ2YSOBkajklW6CFeoyUOwlVAs_Qc_-r3WXUW0,2780
61
68
  stubber/board/pyrightconfig.json,sha256=6oHS4aDOfwKBAFeUPsCGJzEXpUgBZsPaF0M4P-N26D4,1376
62
69
  stubber/bulk/mcu_stubber.py,sha256=_1z0LUzcMbEIXIYInMJcAJZbBPTI8yCHgd1-1nlMscM,16379
63
70
  stubber/codemod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -78,9 +85,9 @@ stubber/commands/cli.py,sha256=pHtViupghgqKMxbyk5_Zz-qHx6i-BGdnTqRO-kngH2E,1850
78
85
  stubber/commands/clone_cmd.py,sha256=mKTfB9eXKX06lTYyon1VioEDIfTPX5dYp-r9Qo87qIg,2673
79
86
  stubber/commands/config_cmd.py,sha256=8g80QAuqBpYpivf35M2XcNxTOqapCRrnIATyen4cg5I,993
80
87
  stubber/commands/enrich_folder_cmd.py,sha256=aGkydAsjyM9LHB99bcjxz_jyDGgOmisZyysbDhZxres,1853
81
- stubber/commands/get_core_cmd.py,sha256=oxiDb_r1Ao9N2maeV0EBKosI4nRDNJkDk52xNunYICM,2226
82
- stubber/commands/get_docstubs_cmd.py,sha256=xTkuc7k7Od7LUtt_ZohoHaRuE6qhQ80ST0b0ZHKMJrM,2824
83
- stubber/commands/get_frozen_cmd.py,sha256=LhtWUL6l2kPUcRQBz19ChwTsdzjai4ryf0UBe52Fu48,3812
88
+ stubber/commands/get_core_cmd.py,sha256=AsD53kuSJmiWUQ9g1d7qFLX7U-Ej_rtyjt3plOLNjjE,2277
89
+ stubber/commands/get_docstubs_cmd.py,sha256=AJkPlVmKRqm5f5ChkcQ3sF86adJdx9N_-P-cR0M1bNg,3001
90
+ stubber/commands/get_frozen_cmd.py,sha256=OyuTXRMbrGG4-CnQpzz9Itli9-dMO5WeEOtX8DIJptk,3941
84
91
  stubber/commands/get_mcu_cmd.py,sha256=uDodn57wdc9rHt7WSmHCFI-zTT1XrebPEaWN-G43E2Y,2015
85
92
  stubber/commands/merge_cmd.py,sha256=2gQYxqapVGD8WMBsqRES7xjIS8acu4hTyutr1OANpAo,1673
86
93
  stubber/commands/publish_cmd.py,sha256=myvgP7Y-pb-nyrMVMelNmIVHbxeK4g1ZXmQtxASblkk,2910
@@ -113,7 +120,7 @@ stubber/publish/merge_docstubs.py,sha256=bo35onpFgcmcqExKSbpbZKprVs2D_feSy0awNZ0
113
120
  stubber/publish/missing_class_methods.py,sha256=i_tPnGpEpmu53N3exPCrwR1HprxxBpgHgLAZSwJCb4o,1823
114
121
  stubber/publish/package.py,sha256=9Op5nPJNOmjTv3QxVeaL86m6Aak2UlUgcQIcDGBbX5w,6320
115
122
  stubber/publish/pathnames.py,sha256=O-9519j6FTIlmTFyDWqCxdG3k8MWhDI89rClBrgUONQ,1811
116
- stubber/publish/publish.py,sha256=4CTI3n2DDU8QnrSiaDT2SAqk73tlziPLE4cLt8saDrk,3698
123
+ stubber/publish/publish.py,sha256=Iguc0e6u0EZY8G33z7JafvKAp1CkvDIXFTBH8oDqNsc,4016
117
124
  stubber/publish/pypi.py,sha256=Mu1F7Xx4e0NPWK1_Erck6QGEfdY8w67EBeWFi6JiAzk,1240
118
125
  stubber/publish/stubpackage.py,sha256=45yquQiBlJhacYEOmAHP6BgDfl1EtUWjdDLx06rCWgE,44731
119
126
  stubber/rst/__init__.py,sha256=5VcbDCotIICa2xnJDs_gw2sFXpjjGOZZbwCrNKXy1OE,293
@@ -132,16 +139,16 @@ stubber/update_fallback.py,sha256=NUxp1sN1-8BLPd0rpExTcYBppUXYWPP8dOcjNeLY2Zo,49
132
139
  stubber/update_module_list.py,sha256=QJ-c2K1Wf2SQdpMeGvlVabasKqsN9ZQV4ye5PbSpsWE,4640
133
140
  stubber/utils/__init__.py,sha256=nV9FsOZsJ_hXsyUv4j0Qqp-PKycxyIktU9IoLs4kQmQ,225
134
141
  stubber/utils/config.py,sha256=Jg4eHhgKim4uP4IqZRWykRNiX1r-SXC2xSrbsVprGAA,4859
135
- stubber/utils/makeversionhdr.py,sha256=ZRpClirIzNK4saZHPaDuEpXhBRI_Ow_yZOxeNM3D_Ro,1908
142
+ stubber/utils/makeversionhdr.py,sha256=dO8sWLAY_ifT1IJJ1-JJ2z_thvfqJnK3c1_bHF_UI28,1953
136
143
  stubber/utils/manifest.py,sha256=XBYCkxtYVAdUcch1784eHPAXlXfyfjco4WnRXxBHZYA,3273
137
144
  stubber/utils/post.py,sha256=oSFZdoP6JwEeAOvsBo3kTk7l8ff_AVtL2Y4VIjzujlE,2837
138
145
  stubber/utils/repos.py,sha256=nNr6Wy-UfIivMPB-1dCzqLJMueqlwQbQA2ha882CnRs,5815
139
146
  stubber/utils/stubmaker.py,sha256=qld_Wfm9f4EuzedXlX1Ky0i0BJdR75oOOTha13_ekz0,5238
140
147
  stubber/utils/typed_config_toml.py,sha256=ikifCIZGNhS_uqsfp6IwIpxdtZqbLtywprjWG_Q0y8o,2629
141
- stubber/utils/versions.py,sha256=R65PAlUCAs9uvTxA6Frco2tHx6ADZCn59JOWI59ZhOo,3790
148
+ stubber/utils/versions.py,sha256=4cGr-4Nbgqzoi-iJC08b_LFZH-CiyTNODlT-ihv4ZE0,3901
142
149
  stubber/variants.py,sha256=-o4TgotbKaCcYBdXkutPaBSR1JdxWmOAiuNT1UlahYc,3784
143
- micropython_stubber-1.20.1.dist-info/entry_points.txt,sha256=NQi_M36fgq5k6giSuASas3LrpF6CVdkzfvJC0ja73_g,55
144
- micropython_stubber-1.20.1.dist-info/LICENSE,sha256=Fx9qrL45ayRXgH6QzttboqZEjKXms0w1t_b_nkOqYCU,1572
145
- micropython_stubber-1.20.1.dist-info/METADATA,sha256=CEvJmltah3TfCSQajDxJwm_DisVNojYKTGNlyDUuAlY,19103
146
- micropython_stubber-1.20.1.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
147
- micropython_stubber-1.20.1.dist-info/RECORD,,
150
+ micropython_stubber-1.20.2.dist-info/entry_points.txt,sha256=NQi_M36fgq5k6giSuASas3LrpF6CVdkzfvJC0ja73_g,55
151
+ micropython_stubber-1.20.2.dist-info/LICENSE,sha256=Fx9qrL45ayRXgH6QzttboqZEjKXms0w1t_b_nkOqYCU,1572
152
+ micropython_stubber-1.20.2.dist-info/METADATA,sha256=tOtMx82j-mRnlMlKbdPSSA_k-UWeJ6WXBbk92utyGdY,19107
153
+ micropython_stubber-1.20.2.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
154
+ micropython_stubber-1.20.2.dist-info/RECORD,,
mpflash/README.md CHANGED
@@ -1,12 +1,24 @@
1
1
  # MPFLASH
2
+ [![pypi version](https://badgen.net/pypi/v/mpflash)](https://pypi.org/project/mpflash/)
3
+ [![python versions](https://badgen.net/pypi/python/mpflash)](https://badgen.net/pypi/python/mpflash)
4
+ [![Downloads](https://static.pepy.tech/badge/mpflash)](https://pepy.tech/project/mpflash)
5
+
2
6
 
3
7
  `mpflash` is a command-line tool for working with MicroPython firmware. It provides features to help you flash and update Micropython on one or more .
4
8
 
5
- This tool was initially created to be used in a CI/CD pipeline to automate the process of downloading and flashing MicroPython firmware to multiple boards, but it has been extend with a TUI to me be used for manual downloadig, flashing and development.
9
+ This tool was initially created to be used in a CI/CD pipeline to automate the process of downloading and flashing MicroPython firmware to multiple boards, but it has been extend with a TUI to be used for manual downloadig, flashing and development.
6
10
 
7
- `mpflash` has been tested on Windows x64, Linux X64, but not (yet) macOS.
8
- Tested ports: `rp2`, `samd`, `esp32`, `esp32s3`, `esp32c3`, `esp8266` and `stm32`
11
+ `mpflash` has been tested on:
12
+ - OS: Windows x64, Linux X64, but not (yet) macOS.
13
+ - Micropython (hardware) ports:
14
+ - `rp2`, using `.uf2`, using filecopy (macos not tested yet)
15
+ - `samd`, using ` .uf2`, using filecopy (macos not tested yet)
16
+ - `esp32`, using `.bin`, using esptool,
17
+ - `esp8266`, using `.bin`, using esptool
18
+ - `stm32`, using ` .dfu`, using pydfu
9
19
 
20
+ Not yet implemented: `nrf`, `cc3200`, `mimxrt`
21
+
10
22
  ## Features
11
23
  1. List the connected boards including their firmware details, in a tabular or json format
12
24
  2. Download MicroPython firmware for versions, and matching a specified board or matches your current attached board.
@@ -34,8 +46,7 @@ On Windows this will not be an issue, but on Linux you can use udev rules to gi
34
46
  ## Detailed usage
35
47
  You can list the connected boards using the following command:
36
48
  ```bash
37
- $ mpflash list
38
- D:\MyPython\micropython-stubber> mpflash list
49
+ $> mpflash list
39
50
  Connected boards
40
51
  ┏━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━┓
41
52
  ┃ Serial ┃Family ┃Port ┃Board ┃CPU ┃Version ┃build ┃
@@ -0,0 +1,98 @@
1
+ import shutil
2
+ from pathlib import Path
3
+ from typing import Union
4
+
5
+ import jsonlines
6
+ import requests
7
+ from loguru import logger as log
8
+
9
+ # re-use logic from mpremote
10
+ from mpremote.mip import _rewrite_url as rewrite_url # type: ignore
11
+
12
+ from mpflash.common import FWInfo
13
+ from mpflash.config import config
14
+ from mpflash.vendor.versions import get_preview_mp_version, get_stable_mp_version
15
+
16
+
17
+ def add_firmware(
18
+ source: Union[Path, str],
19
+ new_fw: FWInfo,
20
+ *,
21
+ force: bool = False,
22
+ custom: bool = False,
23
+ description: str = "",
24
+ ) -> bool:
25
+ """Add a firmware to the firmware folder.
26
+
27
+ stored in the port folder, with the same filename as the source.
28
+
29
+ """
30
+ # Check minimal info needed
31
+ if not new_fw.port or not new_fw.board:
32
+ log.error("Port and board are required")
33
+ return False
34
+ if not isinstance(source, Path) and not source.startswith("http"):
35
+ log.error(f"Invalid source {source}")
36
+ return False
37
+
38
+ # use sensible defaults
39
+ source_2 = Path(source)
40
+ new_fw.ext = new_fw.ext or source_2.suffix
41
+ new_fw.variant = new_fw.variant or new_fw.board
42
+ new_fw.custom = new_fw.custom or custom
43
+ new_fw.description = new_fw.description or description
44
+ if not new_fw.version:
45
+ # TODO: Get version from filename
46
+ # or use the last preview version
47
+ new_fw.version = get_preview_mp_version() if new_fw.preview else get_stable_mp_version()
48
+
49
+ config.firmware_folder.mkdir(exist_ok=True)
50
+
51
+ fw_filename = config.firmware_folder / new_fw.port / source_2.name
52
+
53
+ new_fw.filename = str(fw_filename.relative_to(config.firmware_folder))
54
+ new_fw.firmware = source.as_uri() if isinstance(source, Path) else source
55
+
56
+ if not copy_firmware(source, fw_filename, force):
57
+ log.error(f"Failed to copy {source} to {fw_filename}")
58
+ return False
59
+ # add to inventory
60
+ with jsonlines.open(config.firmware_folder / "firmware.jsonl", "a") as writer:
61
+ log.info(f"Adding {new_fw.port} {new_fw.board}")
62
+ log.info(f" to {fw_filename}")
63
+
64
+ writer.write(new_fw.to_dict())
65
+ return True
66
+
67
+
68
+ def copy_firmware(source: Union[Path, str], fw_filename: Path, force: bool = False):
69
+ """Add a firmware to the firmware folder.
70
+ stored in the port folder, with the same filename as the source.
71
+ """
72
+ if fw_filename.exists() and not force:
73
+ log.error(f" {fw_filename} already exists. Use --force to overwrite")
74
+ return False
75
+ fw_filename.parent.mkdir(exist_ok=True)
76
+ if isinstance(source, Path):
77
+ if not source.exists():
78
+ log.error(f"File {source} does not exist")
79
+ return False
80
+ # file copy
81
+ log.debug(f"Copy {source} to {fw_filename}")
82
+ shutil.copy(source, fw_filename)
83
+ return True
84
+ # handle github urls
85
+ url = rewrite_url(source)
86
+ if str(source).startswith("http://") or str(source).startswith("https://"):
87
+ log.debug(f"Download {url} to {fw_filename}")
88
+ response = requests.get(url)
89
+
90
+ if response.status_code == 200:
91
+ with open(fw_filename, "wb") as file:
92
+ file.write(response.content)
93
+ log.info("File downloaded and saved successfully.")
94
+ return True
95
+ else:
96
+ print("Failed to download the file.")
97
+ return False
98
+ return False
@@ -5,97 +5,85 @@ Note: The prompts can use "{version}" and "{action}" to insert the version and a
5
5
  The values are provided from the answers dictionary.
6
6
  """
7
7
 
8
- from dataclasses import dataclass, field
9
- from pathlib import Path
10
- from typing import Dict, List, Sequence, Tuple, Union
8
+ from typing import List, Sequence, Tuple, Union
11
9
 
12
10
  from loguru import logger as log
13
11
 
14
- from mpflash.config import config
15
- from mpflash.mpboard_id import get_stored_boards_for_port, known_stored_boards, local_mp_ports
16
- from mpflash.mpremoteboard import MPRemoteBoard
17
- from mpflash.vendor.versions import micropython_versions
18
-
19
-
20
- @dataclass
21
- class Params:
22
- ports: List[str] = field(default_factory=list)
23
- boards: List[str] = field(default_factory=list)
24
- versions: List[str] = field(default_factory=list)
25
- fw_folder: Path = Path()
26
-
27
-
28
- @dataclass
29
- class DownloadParams(Params):
30
- clean: bool = False
31
- force: bool = False
32
-
33
-
34
- @dataclass
35
- class FlashParams(Params):
36
- # TODO: Should Serial port be a list?
37
- serial: str = ""
38
- erase: bool = True
39
- bootloader: bool = True
40
- cpu: str = ""
41
-
42
-
43
- ParamType = Union[DownloadParams, FlashParams]
12
+ from .common import DownloadParams, FlashParams, ParamType
13
+ from .config import config
14
+ from .mpboard_id import get_known_boards_for_port, get_known_ports, known_stored_boards
15
+ from .mpremoteboard import MPRemoteBoard
16
+ from .vendor.versions import micropython_versions
44
17
 
45
18
 
46
19
  def ask_missing_params(
47
20
  params: ParamType,
48
- action: str = "download",
49
21
  ) -> ParamType:
50
22
  """
51
23
  Asks the user for parameters that have not been supplied on the commandline and returns the updated params.
52
24
 
53
25
  Args:
54
26
  params (ParamType): The parameters to be updated.
55
- action (str, optional): The action to be performed. Defaults to "download".
56
27
 
57
28
  Returns:
58
29
  ParamType: The updated parameters.
59
30
  """
31
+ import inquirer
32
+
33
+ log.trace(f"ask_missing_params: {params}")
34
+
35
+ # if action flash, single input
36
+ # if action download, multiple input
37
+ multi_select = isinstance(params, DownloadParams)
38
+ action = "download" if isinstance(params, DownloadParams) else "flash"
60
39
  if not config.interactive:
61
40
  # no interactivity allowed
62
41
  return params
63
- # import only when needed to reduce load time
64
- import inquirer
65
42
 
66
43
  questions = []
67
- answers = {"action": action}
68
- if isinstance(params, FlashParams):
44
+ answers: dict[str, Union[str, List]] = {"action": action}
45
+ if not multi_select:
69
46
  if not params.serial or "?" in params.serial:
70
- ask_serialport(questions, action=action)
47
+ questions.append(ask_serialport(multi_select=False, bluetooth=False))
71
48
  else:
72
49
  answers["serial"] = params.serial
73
50
 
74
- if not params.versions or "?" in params.versions:
75
- ask_versions(questions, action=action)
51
+ if params.versions == [] or "?" in params.versions:
52
+ questions.append(ask_mp_version(multi_select=multi_select, action=action))
76
53
  else:
77
54
  # versions is used to show only the boards for the selected versions
78
55
  answers["versions"] = params.versions # type: ignore
79
56
 
80
57
  if not params.boards or "?" in params.boards:
81
- ask_port_board(questions, action=action)
82
-
83
- answers = inquirer.prompt(questions, answers=answers)
58
+ questions.extend(ask_port_board(multi_select=multi_select, action=action))
59
+ if questions:
60
+ answers = inquirer.prompt(questions, answers=answers) # type: ignore
84
61
  if not answers:
85
62
  # input cancelled by user
86
63
  return [] # type: ignore
87
- # print(repr(answers))
64
+ log.trace(f"answers: {answers}")
88
65
  if isinstance(params, FlashParams) and "serial" in answers:
89
- params.serial = answers["serial"]
66
+ if isinstance(answers["serial"], str):
67
+ answers["serial"] = [answers["serial"]]
68
+ params.serial = [s.split()[0] for s in answers["serial"]] # split to remove the description
90
69
  if "port" in answers:
91
- params.ports = [answers["port"]]
70
+ params.ports = [p for p in params.ports if p != "?"] # remove the "?" if present
71
+ params.ports.extend(answers["port"])
92
72
  if "boards" in answers:
93
- params.boards = answers["boards"] if isinstance(answers["boards"], list) else [answers["boards"]]
73
+ params.boards = [b for b in params.boards if b != "?"] # remove the "?" if present
74
+ params.boards.extend(answers["boards"] if isinstance(answers["boards"], list) else [answers["boards"]])
94
75
  if "versions" in answers:
76
+ params.versions = [v for v in params.versions if v != "?"] # remove the "?" if present
95
77
  # make sure it is a list
96
- params.versions = answers["versions"] if isinstance(answers["versions"], list) else [answers["versions"]]
97
-
98
- log.debug(repr(params))
78
+ if isinstance(answers["versions"], (list, tuple)):
79
+ params.versions.extend(answers["versions"])
80
+ else:
81
+ params.versions.append(answers["versions"])
82
+ # remove duplicates
83
+ params.ports = list(set(params.ports))
84
+ params.boards = list(set(params.boards))
85
+ params.versions = list(set(params.versions))
86
+ log.trace(f"ask_missing_params returns: {params}")
99
87
 
100
88
  return params
101
89
 
@@ -110,19 +98,18 @@ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
110
98
  Returns:
111
99
  Sequence[Tuple[str, str]]: The filtered boards.
112
100
  """
101
+ versions = None
113
102
  # if version is not asked ; then need to get the version from the inputs
114
103
  if "versions" in answers:
115
- _versions = list(answers["versions"])
116
- if "stable" in _versions:
117
- _versions.remove("stable")
118
- _versions.append(micropython_versions()[-2]) # latest stable
119
- if "preview" in _versions:
120
- _versions.remove("preview")
121
- _versions.extend((micropython_versions()[-1], micropython_versions()[-2])) # latest preview and stable
122
-
123
- some_boards = known_stored_boards(answers["port"], _versions) # or known_mp_boards(answers["port"])
124
- else:
125
- some_boards = known_stored_boards(answers["port"])
104
+ versions = list(answers["versions"])
105
+ if "stable" in versions:
106
+ versions.remove("stable")
107
+ versions.append(micropython_versions()[-2]) # latest stable
108
+ elif "preview" in versions:
109
+ versions.remove("preview")
110
+ versions.extend((micropython_versions()[-1], micropython_versions()[-2])) # latest preview and stable
111
+
112
+ some_boards = known_stored_boards(answers["port"], versions) # or known_mp_boards(answers["port"])
126
113
 
127
114
  if some_boards:
128
115
  # Create a dictionary where the keys are the second elements of the tuples
@@ -131,11 +118,11 @@ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
131
118
  # Get the values of the dictionary, which are the unique items from the original list
132
119
  some_boards = list(unique_dict.values())
133
120
  else:
134
- some_boards = [(f"No {answers['port']} boards found for version(s) {_versions}", "")]
121
+ some_boards = [(f"No {answers['port']} boards found for version(s) {versions}", "")]
135
122
  return some_boards
136
123
 
137
124
 
138
- def ask_port_board(questions: list, *, action: str):
125
+ def ask_port_board(*, multi_select: bool, action: str):
139
126
  """
140
127
  Asks the user for the port and board selection.
141
128
 
@@ -149,31 +136,28 @@ def ask_port_board(questions: list, *, action: str):
149
136
  # import only when needed to reduce load time
150
137
  import inquirer
151
138
 
152
- # TODO: if action = flash, Use Inquirer.List for boards
153
- inquirer_ux = inquirer.Checkbox if action == "download" else inquirer.List
154
- questions.extend(
155
- (
156
- inquirer.List(
157
- "port",
158
- message="Which port do you want to {action} " + "to {serial} ?" if action == "flash" else "?",
159
- choices=local_mp_ports(),
160
- autocomplete=True,
161
- ),
162
- inquirer_ux(
163
- "boards",
164
- message=(
165
- "Which {port} board firmware do you want to {action} " + "to {serial} ?"
166
- if action == "flash"
167
- else "?"
168
- ),
169
- choices=filter_matching_boards,
170
- validate=lambda _, x: True if x else "Please select at least one board", # type: ignore
139
+ # if action flash, single input
140
+ # if action download, multiple input
141
+ inquirer_ux = inquirer.Checkbox if multi_select else inquirer.List
142
+ return [
143
+ inquirer.List(
144
+ "port",
145
+ message="Which port do you want to {action} " + "to {serial} ?" if action == "flash" else "?",
146
+ choices=get_known_ports(),
147
+ # autocomplete=True,
148
+ ),
149
+ inquirer_ux(
150
+ "boards",
151
+ message=(
152
+ "Which {port} board firmware do you want to {action} " + "to {serial} ?" if action == "flash" else "?"
171
153
  ),
172
- )
173
- )
154
+ choices=filter_matching_boards,
155
+ validate=lambda _, x: True if x else "Please select at least one board", # type: ignore
156
+ ),
157
+ ]
174
158
 
175
159
 
176
- def ask_versions(questions: list, *, action: str):
160
+ def ask_mp_version(multi_select: bool, action: str):
177
161
  """
178
162
  Asks the user for the version selection.
179
163
 
@@ -182,46 +166,43 @@ def ask_versions(questions: list, *, action: str):
182
166
  action (str): The action to be performed.
183
167
 
184
168
  Returns:
185
- None
169
+
186
170
  """
187
171
  # import only when needed to reduce load time
188
172
  import inquirer
189
173
  import inquirer.errors
190
174
 
191
- input_ux = inquirer.Checkbox if action == "download" else inquirer.List
175
+ input_ux = inquirer.Checkbox if multi_select else inquirer.List
176
+
192
177
  mp_versions: List[str] = micropython_versions()
193
- mp_versions = [v for v in mp_versions if "preview" not in v]
178
+ mp_versions.reverse() # newest first
194
179
 
195
180
  # remove the versions for which there are no known boards in the board_info.json
196
181
  # todo: this may be a little slow
197
- mp_versions = [v for v in mp_versions if get_stored_boards_for_port("stm32", [v])]
198
-
199
- mp_versions.append("preview")
200
- mp_versions.reverse() # newest first
182
+ mp_versions = [v for v in mp_versions if "preview" in v or get_known_boards_for_port("stm32", [v])]
201
183
 
202
184
  def at_least_one_validation(answers, current) -> bool:
203
185
  if not current:
204
186
  raise inquirer.errors.ValidationError("", reason="Please select at least one version")
205
- if isinstance(current, list):
206
- if not any(current):
207
- raise inquirer.errors.ValidationError("", reason="Please select at least one version")
187
+ if isinstance(current, list) and not any(current):
188
+ raise inquirer.errors.ValidationError("", reason="Please select at least one version")
208
189
  return True
209
190
 
210
- questions.append(
211
- input_ux(
212
- # inquirer.List(
213
- "versions",
214
- message="Which version(s) do you want to {action} " + ("to {serial} ?" if action == "flash" else "?"),
215
- # Hints would be nice , but needs a hint for each and every option
216
- # hints=["Use space to select multiple options"],
217
- choices=mp_versions,
218
- autocomplete=True,
219
- validate=at_least_one_validation,
220
- )
191
+ message = "Which version(s) do you want to {action} " + ("to {serial} ?" if action == "flash" else "?")
192
+ q = input_ux(
193
+ # inquirer.List(
194
+ "versions",
195
+ message=message,
196
+ # Hints would be nice , but needs a hint for each and every option
197
+ # hints=["Use space to select multiple options"],
198
+ choices=mp_versions,
199
+ autocomplete=True,
200
+ validate=at_least_one_validation, # type: ignore
221
201
  )
202
+ return q
222
203
 
223
204
 
224
- def ask_serialport(questions: list, *, action: str):
205
+ def ask_serialport(*, multi_select: bool = False, bluetooth: bool = False):
225
206
  """
226
207
  Asks the user for the serial port selection.
227
208
 
@@ -235,15 +216,11 @@ def ask_serialport(questions: list, *, action: str):
235
216
  # import only when needed to reduce load time
236
217
  import inquirer
237
218
 
238
- serialports = MPRemoteBoard.connected_boards()
239
- questions.append(
240
- inquirer.List(
241
- "serial",
242
- message="Which serial port do you want to {action} ?",
243
- choices=serialports,
244
- other=True,
245
- validate=lambda _, x: True if x else "Please select or enter a serial port", # type: ignore
246
- )
219
+ comports = MPRemoteBoard.connected_boards(bluetooth=bluetooth, description=True)
220
+ return inquirer.List(
221
+ "serial",
222
+ message="Which serial port do you want to {action} ?",
223
+ choices=comports,
224
+ other=True,
225
+ validate=lambda _, x: True if x else "Please select or enter a serial port", # type: ignore
247
226
  )
248
-
249
- return questions