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
@@ -18,9 +18,11 @@ from bs4 import BeautifulSoup
18
18
  from loguru import logger as log
19
19
  from rich.progress import track
20
20
 
21
- from mpflash.common import PORT_FWTYPES
21
+ from mpflash.common import PORT_FWTYPES, FWInfo
22
22
  from mpflash.errors import MPFlashError
23
+ from mpflash.mpboard_id import get_known_ports
23
24
 
25
+ # avoid conflict with the ujson used by MicroPython
24
26
  jsonlines.ujson = None # type: ignore
25
27
  # #########################################################################################################
26
28
 
@@ -50,6 +52,10 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
50
52
 
51
53
  Args:
52
54
  page_url (str): The url of the page to get the board urls from.
55
+
56
+ Returns:
57
+ List[Dict[str, str]]: A list of dictionaries containing the board name and url.
58
+
53
59
  """
54
60
  downloads_html = get_page(page_url)
55
61
  soup = BeautifulSoup(downloads_html, "html.parser")
@@ -88,14 +94,10 @@ def board_firmware_urls(board_url: str, base_url: str, ext: str) -> List[str]:
88
94
  return links
89
95
 
90
96
 
91
- # type alias for the firmware info
92
- FirmwareInfo = Dict[str, str]
93
-
94
-
95
97
  # boards we are interested in ( this avoids getting a lot of boards we don't care about)
96
98
  # The first run takes ~60 seconds to run for 4 ports , all boards
97
99
  # so it makes sense to cache the results and skip boards as soon as possible
98
- def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FirmwareInfo]:
100
+ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]:
99
101
  """
100
102
  Retrieves a list of firmware information for the specified ports and boards.
101
103
 
@@ -105,57 +107,68 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[Firmwar
105
107
  clean (bool): A flag indicating whether to perform a clean retrieval.
106
108
 
107
109
  Returns:
108
- List[FirmwareInfo]: A list of firmware information for the specified ports and boards.
110
+ List[FWInfo]: A list of firmware information for the specified ports and boards.
109
111
 
110
112
  """
111
- board_urls: List[FirmwareInfo] = []
113
+ board_urls: List[FWInfo] = []
114
+ if ports is None:
115
+ ports = get_known_ports()
112
116
  for port in ports:
113
117
  download_page_url = f"{MICROPYTHON_ORG_URL}download/?port={port}"
114
- _urls = get_board_urls(download_page_url)
118
+ urls = get_board_urls(download_page_url)
115
119
  # filter out boards we don't care about
116
- _urls = [board for board in _urls if board["board"] in boards]
120
+ urls = [board for board in urls if board["board"] in boards]
117
121
  # add the port to the board urls
118
- for board in _urls:
122
+ for board in urls:
119
123
  board["port"] = port
120
124
 
121
- for board in track(_urls, description=f"Checking {port} download pages", transient=True):
125
+ for board in track(urls, description=f"Checking {port} download pages", transient=True, refresh_per_second=2):
122
126
  # add a board to the list for each firmware found
123
- firmwares = []
127
+ firmware_urls: List[str] = []
124
128
  for ext in PORT_FWTYPES[port]:
125
- firmwares += board_firmware_urls(board["url"], MICROPYTHON_ORG_URL, ext)
126
-
127
- for _url in firmwares:
129
+ firmware_urls += board_firmware_urls(board["url"], MICROPYTHON_ORG_URL, ext)
130
+ for _url in firmware_urls:
128
131
  board["firmware"] = _url
129
- board["preview"] = "preview" in _url # type: ignore
130
- if ver_match := re.search(RE_VERSION_PREVIEW, _url):
131
- board["version"] = ver_match[1]
132
- else:
133
- board["version"] = ""
134
- if "preview." in board["version"]:
135
- board["build"] = board["version"].split("preview.")[-1]
136
- else:
137
- board["build"] = "0"
138
132
  fname = Path(board["firmware"]).name
139
133
  if clean:
140
134
  # remove date from firmware name
141
135
  fname = re.sub(RE_DATE, "-", fname)
142
136
  # remove hash from firmware name
143
137
  fname = re.sub(RE_HASH, ".", fname)
144
- board["filename"] = fname
145
- board["ext"] = Path(board["firmware"]).suffix
146
- board["variant"] = board["filename"].split("-v")[0] if "-v" in board["filename"] else ""
147
- board_urls.append(board.copy())
138
+ fw_info = FWInfo(
139
+ filename=fname,
140
+ port=port,
141
+ board=board["board"],
142
+ preview="preview" in _url,
143
+ firmware=_url,
144
+ version="",
145
+ )
146
+ # board["firmware"] = _url
147
+ # board["preview"] = "preview" in _url # type: ignore
148
+ if ver_match := re.search(RE_VERSION_PREVIEW, _url):
149
+ fw_info.version = ver_match[1]
150
+ # else:
151
+ # board.$1= ""
152
+ if "preview." in fw_info.version:
153
+ fw_info.build = fw_info.version.split("preview.")[-1]
154
+ else:
155
+ fw_info.build = "0"
156
+
157
+ fw_info.ext = Path(fw_info.firmware).suffix
158
+ fw_info.variant = fw_info.filename.split("-v")[0] if "-v" in fw_info.filename else ""
159
+
160
+ board_urls.append(fw_info)
148
161
  return board_urls
149
162
 
150
163
 
151
- def key_fw_ver_pre_ext_bld(x: FirmwareInfo):
164
+ def key_fw_ver_pre_ext_bld(x: FWInfo):
152
165
  "sorting key for the retrieved board urls"
153
- return x["variant"], x["version"], x["preview"], x["ext"], x["build"]
166
+ return x.variant, x.version, x.preview, x.ext, x.build
154
167
 
155
168
 
156
- def key_fw_var_pre_ext(x: FirmwareInfo):
169
+ def key_fw_var_pre_ext(x: FWInfo):
157
170
  "Grouping key for the retrieved board urls"
158
- return x["variant"], x["preview"], x["ext"]
171
+ return x.variant, x.preview, x.ext
159
172
 
160
173
 
161
174
  def download_firmwares(
@@ -170,10 +183,11 @@ def download_firmwares(
170
183
  skipped = downloaded = 0
171
184
  if versions is None:
172
185
  versions = []
186
+
173
187
  unique_boards = get_firmware_list(ports, boards, versions, clean)
174
188
 
175
189
  for b in unique_boards:
176
- log.debug(b["filename"])
190
+ log.debug(b.filename)
177
191
  # relevant
178
192
 
179
193
  log.info(f"Found {len(unique_boards)} relevant unique firmwares")
@@ -182,23 +196,23 @@ def download_firmwares(
182
196
 
183
197
  with jsonlines.open(firmware_folder / "firmware.jsonl", "a") as writer:
184
198
  for board in unique_boards:
185
- filename = firmware_folder / board["port"] / board["filename"]
199
+ filename = firmware_folder / board.port / board.filename
186
200
  filename.parent.mkdir(exist_ok=True)
187
201
  if filename.exists() and not force:
188
202
  skipped += 1
189
203
  log.debug(f" {filename} already exists, skip download")
190
204
  continue
191
- log.info(f"Downloading {board['firmware']}")
205
+ log.info(f"Downloading {board.firmware}")
192
206
  log.info(f" to {filename}")
193
207
  try:
194
- r = requests.get(board["firmware"], allow_redirects=True)
208
+ r = requests.get(board.firmware, allow_redirects=True)
195
209
  with open(filename, "wb") as fw:
196
210
  fw.write(r.content)
197
- board["filename"] = str(filename.relative_to(firmware_folder))
211
+ board.filename = str(filename.relative_to(firmware_folder))
198
212
  except requests.RequestException as e:
199
213
  log.exception(e)
200
214
  continue
201
- writer.write(board)
215
+ writer.write(board.to_dict())
202
216
  downloaded += 1
203
217
  log.info(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
204
218
  return downloaded + skipped
@@ -215,7 +229,7 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
215
229
  clean : A flag indicating whether to perform a clean check.
216
230
 
217
231
  Returns:
218
- List[FirmwareInfo]: A list of unique firmware information.
232
+ List[FWInfo]: A list of unique firmware information.
219
233
 
220
234
  """
221
235
 
@@ -227,12 +241,12 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
227
241
  relevant = [
228
242
  board
229
243
  for board in board_urls
230
- if board["board"] in boards and (board["version"] in versions or board["preview"] and preview)
244
+ if board.board in boards and (board.version in versions or board.preview and preview)
231
245
  # and b["port"] in ["esp32", "rp2"]
232
246
  ]
233
247
  log.debug(f"Matching firmwares: {len(relevant)}")
234
248
  # select the unique boards
235
- unique_boards: List[FirmwareInfo] = []
249
+ unique_boards: List[FWInfo] = []
236
250
  for _, g in itertools.groupby(relevant, key=key_fw_var_pre_ext):
237
251
  # list is aleady sorted by build so we can just get the last item
238
252
  sub_list = list(g)
@@ -16,11 +16,11 @@ def downloaded_firmwares(fw_folder: Path) -> List[FWInfo]:
16
16
  firmwares: List[FWInfo] = []
17
17
  try:
18
18
  with jsonlines.open(fw_folder / "firmware.jsonl") as reader:
19
- firmwares.extend(iter(reader))
19
+ firmwares = [FWInfo.from_dict(item) for item in reader]
20
20
  except FileNotFoundError:
21
21
  log.error(f"No firmware.jsonl found in {fw_folder}")
22
22
  # sort by filename
23
- firmwares.sort(key=lambda x: x["filename"])
23
+ firmwares.sort(key=lambda x: x.filename)
24
24
  return firmwares
25
25
 
26
26
 
@@ -68,7 +68,7 @@ def find_downloaded_firmware(
68
68
  )
69
69
  # hope we have a match now for the board
70
70
  # sort by filename
71
- fw_list.sort(key=lambda x: x["filename"])
71
+ fw_list.sort(key=lambda x: x.filename)
72
72
  return fw_list
73
73
 
74
74
 
@@ -84,23 +84,23 @@ def filter_downloaded_fwlist(
84
84
  """Filter the downloaded firmware list based on the provided parameters"""
85
85
  if "preview" in version:
86
86
  # never get a preview for an older version
87
- fw_list = [fw for fw in fw_list if fw["preview"]]
87
+ fw_list = [fw for fw in fw_list if fw.preview]
88
88
  else:
89
- fw_list = [fw for fw in fw_list if fw["version"] == version]
89
+ fw_list = [fw for fw in fw_list if fw.version == version]
90
90
 
91
91
  # filter by port
92
92
  if port:
93
- fw_list = [fw for fw in fw_list if fw["port"] == port]
93
+ fw_list = [fw for fw in fw_list if fw.port == port]
94
94
 
95
95
  if board_id:
96
96
  if variants:
97
97
  # any variant of this board_id
98
- fw_list = [fw for fw in fw_list if fw["board"] == board_id]
98
+ fw_list = [fw for fw in fw_list if fw.board == board_id]
99
99
  else:
100
100
  # the firmware variant should match exactly the board_id
101
- fw_list = [fw for fw in fw_list if fw["variant"] == board_id]
101
+ fw_list = [fw for fw in fw_list if fw.variant == board_id]
102
102
  if selector and port in selector:
103
- fw_list = [fw for fw in fw_list if fw["filename"].endswith(selector[port])]
103
+ fw_list = [fw for fw in fw_list if fw.filename.endswith(selector[port])]
104
104
  return fw_list
105
105
 
106
106
 
mpflash/mpflash/flash.py CHANGED
@@ -23,11 +23,11 @@ def flash_list(
23
23
  """Flash a list of boards with the specified firmware."""
24
24
  flashed = []
25
25
  for mcu, fw_info in todo:
26
- fw_file = fw_folder / fw_info["filename"] # type: ignore
26
+ fw_file = fw_folder / fw_info.filename
27
27
  if not fw_file.exists():
28
28
  log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
29
29
  continue
30
- log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info['version']}")
30
+ log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info.version}")
31
31
  updated = None
32
32
  # try:
33
33
  if mcu.port in [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts] and fw_file.suffix == ".uf2":
@@ -10,7 +10,7 @@ from typing import List, Optional
10
10
  import esptool
11
11
  from loguru import logger as log
12
12
 
13
- from mpflash.mpboard_id import find_stored_board
13
+ from mpflash.mpboard_id import find_known_board
14
14
  from mpflash.mpremoteboard import MPRemoteBoard
15
15
 
16
16
 
@@ -22,7 +22,7 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
22
22
  log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
23
23
  if not mcu.cpu:
24
24
  # Lookup CPU based on the board name
25
- mcu.cpu = find_stored_board(mcu.board)["cpu"]
25
+ mcu.cpu = find_known_board(mcu.board).cpu
26
26
 
27
27
  cmds: List[List[str]] = []
28
28
  if erase:
@@ -14,7 +14,9 @@ from rich.progress import track
14
14
  from mpflash.mpremoteboard import MPRemoteBoard
15
15
 
16
16
  from .common import PORT_FWTYPES
17
- from .flash_uf2_linux import dismount_uf2, wait_for_UF2_linux
17
+ from .config import config
18
+ from .flash_uf2_linux import dismount_uf2_linux, wait_for_UF2_linux
19
+ from .flash_uf2_macos import wait_for_UF2_macos
18
20
  from .flash_uf2_windows import wait_for_UF2_windows
19
21
 
20
22
 
@@ -27,9 +29,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
27
29
  - copy the firmware file to the drive
28
30
  - wait for the device to restart (5s)
29
31
 
30
- for Lunix :
31
- pmount and pumount are used to mount and unmount the drive
32
- as this is not done automatically by the OS in headless mode.
32
+ for Linux - to support headless operation ( GH Actions ) :
33
+ pmount and pumount are used to mount and unmount the drive
34
+ as this is not done automatically by the OS in headless mode.
33
35
  """
34
36
  if ".uf2" not in PORT_FWTYPES[mcu.port]:
35
37
  log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
@@ -41,9 +43,15 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
41
43
  destination = wait_for_UF2_linux()
42
44
  elif sys.platform == "win32":
43
45
  destination = wait_for_UF2_windows()
46
+ elif sys.platform == "darwin":
47
+ log.warning(f"OS {sys.platform} not tested/supported")
48
+ # TODO: test which of the options is best
49
+ if "macos_uf2" in config.tests:
50
+ destination = wait_for_UF2_macos()
51
+ else:
52
+ destination = wait_for_UF2_linux()
44
53
  else:
45
54
  log.warning(f"OS {sys.platform} not tested/supported")
46
- destination = wait_for_UF2_linux()
47
55
  return None
48
56
 
49
57
  if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
@@ -54,8 +62,8 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
54
62
  log.info(f"Copying {fw_file} to {destination}.")
55
63
  shutil.copy(fw_file, destination)
56
64
  log.success("Done copying, resetting the board and wait for it to restart")
57
- if sys.platform in ["linux", "darwin"]:
58
- dismount_uf2()
59
- for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True):
65
+ if sys.platform in ["linux"]:
66
+ dismount_uf2_linux()
67
+ for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True, refresh_per_second=2):
60
68
  time.sleep(1) # 5 secs to short on linux
61
69
  return mcu
@@ -13,28 +13,15 @@ from loguru import logger as log
13
13
  from rich.progress import track
14
14
 
15
15
  from .flash_uf2_boardid import get_board_id
16
+ from .uf2disk import UF2Disk
16
17
 
17
18
  glb_dismount_me: List[UF2Disk] = []
18
19
 
19
20
 
20
- class UF2Disk:
21
- """Info to support mounting and unmounting of UF2 drives on linux"""
22
-
23
- device_path: str
24
- label: str
25
- mountpoint: str
26
-
27
- def __repr__(self):
28
- return repr(self.__dict__)
29
-
30
-
31
21
  def get_uf2_drives():
32
22
  """
33
23
  Get a list of all the (un)mounted UF2 drives
34
24
  """
35
- if sys.platform != "linux":
36
- log.error("pumount only works on Linux")
37
- return
38
25
  # import blkinfo only on linux
39
26
  from blkinfo import BlkDiskInfo
40
27
 
@@ -101,7 +88,7 @@ def pumount(disk: UF2Disk):
101
88
  log.warning(f"{disk.label} already dismounted")
102
89
 
103
90
 
104
- def dismount_uf2():
91
+ def dismount_uf2_linux():
105
92
  global glb_dismount_me
106
93
  for disk in glb_dismount_me:
107
94
  pumount(disk)
@@ -113,7 +100,9 @@ def wait_for_UF2_linux(s_max: int = 10):
113
100
  wait = 10
114
101
  uf2_drives = []
115
102
  # while not destination and wait > 0:
116
- for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True):
103
+ for _ in track(
104
+ range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
105
+ ):
117
106
  # log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
118
107
  uf2_drives += list(get_uf2_drives())
119
108
  for drive in get_uf2_drives():
@@ -0,0 +1,78 @@
1
+ """ Flashing UF2 based MCU on macos"""
2
+
3
+ # sourcery skip: snake-case-functions
4
+ from __future__ import annotations
5
+
6
+ import sys
7
+ import time
8
+ from pathlib import Path
9
+
10
+ from loguru import logger as log
11
+ from rich.progress import track
12
+
13
+ from .flash_uf2_boardid import get_board_id
14
+ from .uf2disk import UF2Disk
15
+
16
+
17
+ def get_uf2_drives():
18
+ """
19
+ Get a list of all the (un)mounted UF2 drives
20
+ """
21
+ if sys.platform != "linux":
22
+ log.error("pumount only works on Linux")
23
+ return
24
+ # import blkinfo only on linux
25
+ from blkinfo import BlkDiskInfo
26
+
27
+ myblkd = BlkDiskInfo()
28
+ filters = {
29
+ "tran": "usb",
30
+ }
31
+ usb_disks = myblkd.get_disks(filters)
32
+ for disk in usb_disks:
33
+ if disk["fstype"] == "vfat":
34
+ uf2_part = disk
35
+ # unpartioned usb disk or partition (e.g. /dev/sdb )
36
+ # SEEED WIO Terminal is unpartioned
37
+ # print( json.dumps(uf2_part, indent=4))
38
+ uf2 = UF2Disk()
39
+ uf2.device_path = "/dev/" + uf2_part["name"]
40
+ uf2.label = uf2_part["label"]
41
+ uf2.mountpoint = uf2_part["mountpoint"]
42
+ yield uf2
43
+ elif disk["type"] == "disk" and disk.get("children") and len(disk.get("children")) > 0:
44
+ if disk.get("children")[0]["type"] == "part" and disk.get("children")[0]["fstype"] == "vfat":
45
+ uf2_part = disk.get("children")[0]
46
+ # print( json.dumps(uf2_part, indent=4))
47
+ uf2 = UF2Disk()
48
+ uf2.device_path = "/dev/" + uf2_part["name"]
49
+ uf2.label = uf2_part["label"]
50
+ uf2.mountpoint = uf2_part["mountpoint"]
51
+ yield uf2
52
+
53
+
54
+ def wait_for_UF2_macos(s_max: int = 10):
55
+ destination = ""
56
+ wait = 10
57
+ uf2_drives = []
58
+ # while not destination and wait > 0:
59
+ for _ in track(
60
+ range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
61
+ ):
62
+ # log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
63
+ uf2_drives += list(get_uf2_drives())
64
+ for drive in get_uf2_drives():
65
+ time.sleep(1)
66
+ try:
67
+ if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
68
+ board_id = get_board_id(Path(drive.mountpoint)) # type: ignore
69
+ destination = Path(drive.mountpoint)
70
+ break
71
+ except PermissionError:
72
+ log.debug(f"Permission error on {drive.mountpoint}")
73
+ continue
74
+ if destination:
75
+ break
76
+ time.sleep(1)
77
+ wait -= 1
78
+ return destination
@@ -17,7 +17,7 @@ def wait_for_UF2_windows(s_max: int = 10):
17
17
  if s_max < 1:
18
18
  s_max = 10
19
19
  destination = ""
20
- for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True):
20
+ for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True,refresh_per_second=2):
21
21
  # log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
22
22
  drives = [drive.device for drive in psutil.disk_partitions()]
23
23
  for drive in drives:
mpflash/mpflash/list.py CHANGED
@@ -1,89 +1,89 @@
1
1
  from typing import List
2
2
 
3
- from rich import print
4
- from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn, track
5
- from rich.table import Column, Table
3
+ from rich.progress import track
4
+ from rich.table import Table
6
5
 
7
6
  from mpflash.mpremoteboard import MPRemoteBoard
8
7
  from mpflash.vendor.versions import clean_version
9
8
 
10
- from .config import config
11
9
  from .logger import console
12
10
 
13
- rp_spinner = SpinnerColumn(finished_text="✅")
14
- rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Column())
15
- rp_bar = BarColumn(bar_width=None, table_column=Column())
16
11
 
12
+ def show_mcus(
13
+ conn_mcus: List[MPRemoteBoard],
14
+ title: str = "Connected boards",
15
+ refresh: bool = True,
16
+ ):
17
+ console.print(mcu_table(conn_mcus, title, refresh))
17
18
 
18
- def list_mcus(bluetooth: bool = False):
19
- """
20
- Retrieves information about connected microcontroller boards.
21
-
22
- Returns:
23
- List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
24
- Raises:
25
- ConnectionError: If there is an error connecting to a board.
26
- """
27
- conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
28
19
 
29
- # a lot of boilerplate to show a progress bar with the comport currenlty scanned
30
- with Progress(rp_spinner, rp_text, rp_bar, TimeElapsedColumn()) as progress:
31
- tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
32
- progress.tasks[tsk_scan].fields["device"] = "..."
33
- progress.tasks[tsk_scan].visible = True
34
- progress.start_task(tsk_scan)
35
- try:
36
- for mcu in conn_mcus:
37
- progress.update(tsk_scan, device=mcu.serialport.replace("/dev/", ""))
38
- try:
39
- mcu.get_mcu_info()
40
- except ConnectionError as e:
41
- print(f"Error: {e}")
42
- continue
43
- finally:
44
- # transient
45
- progress.stop_task(tsk_scan)
46
- progress.tasks[tsk_scan].visible = False
47
- return conn_mcus
20
+ def abbrv_family(family: str, is_wide: bool) -> str:
21
+ if not is_wide:
22
+ ABRV = {"micropython": "upy", "circuitpython": "cpy", "unknown": "?"}
23
+ return ABRV.get(family, family[:4])
24
+ return family
48
25
 
49
26
 
50
- def show_mcus(
27
+ def mcu_table(
51
28
  conn_mcus: List[MPRemoteBoard],
52
29
  title: str = "Connected boards",
53
30
  refresh: bool = True,
54
- ): # sourcery skip: extract-duplicate-method
55
- """Show the list of connected boards in a nice table"""
31
+ ):
32
+ """
33
+ builds a rich table with the connected boards information
34
+ The columns of the table are adjusted to the terminal width
35
+ the columns are :
36
+ Narrow Wide
37
+ - Serial Yes Yes
38
+ - Family abbrv. Yes
39
+ - Port - yes
40
+ - Board Yes Yes BOARD_ID and Description
41
+ - CPU - Yes
42
+ - Version Yes Yes
43
+ - Build * * only if any of the mcus have a build
44
+ """
56
45
  table = Table(
57
46
  title=title,
58
47
  title_style="magenta",
59
48
  header_style="bold magenta",
60
49
  collapse_padding=True,
61
- width=110,
50
+ padding=(0, 0),
62
51
  )
63
- table.add_column("Serial", overflow="fold")
64
- table.add_column("Family")
65
- table.add_column("Port")
52
+ # check if the terminal is wide enough to show all columns or if we need to collapse some
53
+ is_wide = console.width > 99
54
+ needs_build = any(mcu.build for mcu in conn_mcus)
55
+
56
+ table.add_column("Serial" if is_wide else "Ser.", overflow="fold")
57
+ table.add_column("Family" if is_wide else "Fam.", overflow="crop", max_width=None if is_wide else 4)
58
+ if is_wide:
59
+ table.add_column("Port")
66
60
  table.add_column("Board", overflow="fold")
67
61
  # table.add_column("Variant") # TODO: add variant
68
- table.add_column("CPU")
69
- table.add_column("Version")
70
- table.add_column("build", justify="right")
62
+ if is_wide:
63
+ table.add_column("CPU")
64
+ table.add_column("Version", overflow="fold", min_width=5, max_width=16)
65
+ if needs_build:
66
+ table.add_column("Build" if is_wide else "Bld", justify="right")
71
67
 
72
- for mcu in track(conn_mcus, description="Updating board info", transient=True, update_period=0.1):
68
+ for mcu in track(conn_mcus, description="Updating board info", transient=True, refresh_per_second=2):
73
69
  if refresh:
74
70
  try:
75
71
  mcu.get_mcu_info()
76
72
  except ConnectionError:
77
73
  continue
78
74
  description = f"[italic bright_cyan]{mcu.description}" if mcu.description else ""
79
- table.add_row(
75
+ row = [
80
76
  mcu.serialport.replace("/dev/", ""),
81
- mcu.family,
82
- mcu.port,
83
- f"{mcu.board}\n{description}".strip(),
84
- # mcu.variant,
85
- mcu.cpu,
86
- clean_version(mcu.version),
87
- mcu.build,
88
- )
89
- console.print(table)
77
+ abbrv_family(mcu.family, is_wide),
78
+ ]
79
+ if is_wide:
80
+ row.append(mcu.port)
81
+ row.append(f"{mcu.board}\n{description}".strip())
82
+ if is_wide:
83
+ row.append(mcu.cpu)
84
+ row.append(clean_version(mcu.version))
85
+ if needs_build:
86
+ row.append(mcu.build)
87
+
88
+ table.add_row(*row)
89
+ return table