micropython-stubber 1.20.2__py3-none-any.whl → 1.20.5__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 (51) hide show
  1. {micropython_stubber-1.20.2.dist-info → micropython_stubber-1.20.5.dist-info}/METADATA +5 -4
  2. {micropython_stubber-1.20.2.dist-info → micropython_stubber-1.20.5.dist-info}/RECORD +51 -46
  3. {micropython_stubber-1.20.2.dist-info → micropython_stubber-1.20.5.dist-info}/WHEEL +1 -1
  4. mpflash/mpflash/ask_input.py +24 -14
  5. mpflash/mpflash/bootloader/__init__.py +36 -0
  6. mpflash/mpflash/bootloader/manual.py +102 -0
  7. mpflash/mpflash/bootloader/micropython.py +10 -0
  8. mpflash/mpflash/bootloader/touch1200.py +45 -0
  9. mpflash/mpflash/cli_download.py +1 -0
  10. mpflash/mpflash/cli_flash.py +16 -9
  11. mpflash/mpflash/cli_group.py +13 -6
  12. mpflash/mpflash/cli_list.py +6 -2
  13. mpflash/mpflash/cli_main.py +5 -2
  14. mpflash/mpflash/common.py +15 -2
  15. mpflash/mpflash/config.py +27 -1
  16. mpflash/mpflash/download.py +82 -16
  17. mpflash/mpflash/downloaded.py +28 -7
  18. mpflash/mpflash/errors.py +5 -1
  19. mpflash/mpflash/flash.py +10 -27
  20. mpflash/mpflash/flash_uf2.py +4 -6
  21. mpflash/mpflash/flash_uf2_boardid.py +2 -1
  22. mpflash/mpflash/flash_uf2_macos.py +13 -57
  23. mpflash/mpflash/flash_uf2_windows.py +4 -4
  24. mpflash/mpflash/mpboard_id/__init__.py +4 -0
  25. mpflash/mpflash/mpboard_id/board_id.py +19 -3
  26. mpflash/mpflash/mpboard_id/store.py +2 -1
  27. mpflash/mpflash/vendor/click_aliases.py +91 -0
  28. mpflash/poetry.lock +102 -137
  29. mpflash/pyproject.toml +1 -1
  30. stubber/__init__.py +1 -1
  31. stubber/board/createstubs.py +12 -4
  32. stubber/board/createstubs_db.py +4 -5
  33. stubber/board/createstubs_db_min.py +1 -1
  34. stubber/board/createstubs_db_mpy.mpy +0 -0
  35. stubber/board/createstubs_mem.py +4 -5
  36. stubber/board/createstubs_mem_min.py +1 -1
  37. stubber/board/createstubs_mem_mpy.mpy +0 -0
  38. stubber/board/createstubs_min.py +2 -2
  39. stubber/board/createstubs_mpy.mpy +0 -0
  40. stubber/board/modulelist.txt +9 -0
  41. stubber/bulk/mcu_stubber.py +0 -1
  42. stubber/codemod/_partials/__init__.py +0 -2
  43. stubber/publish/candidates.py +7 -28
  44. stubber/publish/enums.py +0 -6
  45. stubber/publish/package.py +15 -46
  46. stubber/publish/publish.py +1 -2
  47. stubber/rst/lookup.py +7 -7
  48. stubber/rst/reader.py +26 -27
  49. stubber/update_module_list.py +2 -26
  50. {micropython_stubber-1.20.2.dist-info → micropython_stubber-1.20.5.dist-info}/LICENSE +0 -0
  51. {micropython_stubber-1.20.2.dist-info → micropython_stubber-1.20.5.dist-info}/entry_points.txt +0 -0
@@ -19,12 +19,12 @@ def mpflash():
19
19
  cli.add_command(cli_flash_board)
20
20
 
21
21
  # cli(auto_envvar_prefix="MPFLASH")
22
- if False and os.environ.get("COMPUTERNAME") == "JOSVERL-S4":
22
+ if False and os.environ.get("COMPUTERNAME").startswith("JOSVERL"):
23
23
  # intentional less error suppression on dev machine
24
24
  result = cli(standalone_mode=False)
25
25
  else:
26
26
  try:
27
- result = cli(standalone_mode=False)
27
+ result = cli(standalone_mode=True)
28
28
  exit(result)
29
29
  except AttributeError as e:
30
30
  log.error(f"Error: {e}")
@@ -32,6 +32,9 @@ def mpflash():
32
32
  except click.exceptions.ClickException as e:
33
33
  log.error(f"Error: {e}")
34
34
  exit(-2)
35
+ except click.exceptions.Abort as e:
36
+ # Aborted - Ctrl-C
37
+ exit(-3)
35
38
 
36
39
 
37
40
  if __name__ == "__main__":
mpflash/mpflash/common.py CHANGED
@@ -2,6 +2,7 @@ import fnmatch
2
2
  import os
3
3
  import sys
4
4
  from dataclasses import dataclass, field
5
+ from enum import Enum
5
6
  from pathlib import Path
6
7
  from typing import List, Optional, Union
7
8
 
@@ -47,7 +48,7 @@ class FWInfo:
47
48
  firmware: str = field(default="") # url or path to original firmware image
48
49
  variant: str = field(default="") # MicroPython variant
49
50
  preview: bool = field(default=False) # True if the firmware is a preview version
50
- version: str = field(default="") # MicroPython version
51
+ version: str = field(default="") # MicroPython version (NO v prefix)
51
52
  url: str = field(default="") # url to the firmware image download folder
52
53
  build: str = field(default="0") # The build = number of commits since the last release
53
54
  ext: str = field(default="") # the file extension of the firmware
@@ -90,14 +91,26 @@ class DownloadParams(Params):
90
91
  force: bool = False
91
92
 
92
93
 
94
+ class BootloaderMethod(Enum):
95
+ MANUAL = "manual"
96
+ MPY = "mpy"
97
+ TOUCH_1200 = "touch1200"
98
+ NONE = "none"
99
+
100
+
101
+
93
102
  @dataclass
94
103
  class FlashParams(Params):
95
104
  """Parameters for flashing a board"""
96
105
 
97
106
  erase: bool = True
98
- bootloader: bool = True
107
+ bootloader: BootloaderMethod = BootloaderMethod.NONE
99
108
  cpu: str = ""
100
109
 
110
+ def __post_init__(self):
111
+ if isinstance(self.bootloader, str):
112
+ self.bootloader = BootloaderMethod(self.bootloader)
113
+
101
114
 
102
115
  ParamType = Union[DownloadParams, FlashParams]
103
116
 
mpflash/mpflash/config.py CHANGED
@@ -1,10 +1,22 @@
1
1
  """centralized configuration for mpflash"""
2
2
 
3
+ import os
3
4
  from pathlib import Path
4
5
  from typing import List
5
6
 
7
+ import pkg_resources
6
8
  import platformdirs
7
9
 
10
+ from mpflash.logger import log
11
+
12
+
13
+ def get_version():
14
+ name = __package__ or "mpflash"
15
+ try:
16
+ return pkg_resources.get_distribution(name).version
17
+ except pkg_resources.DistributionNotFound:
18
+ return "Package not found"
19
+
8
20
 
9
21
  class MPtoolConfig:
10
22
  """Centralized configuration for mpflash"""
@@ -12,10 +24,24 @@ class MPtoolConfig:
12
24
  quiet: bool = False
13
25
  verbose: bool = False
14
26
  ignore_ports: List[str] = []
15
- interactive: bool = True
16
27
  firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
17
28
  # test options specified on the commandline
18
29
  tests: List[str] = []
30
+ _interactive: bool = True
31
+
32
+ @property
33
+ def interactive(self):
34
+ # No interactions in CI
35
+ if os.getenv('GITHUB_ACTIONS') == 'true':
36
+ log.warning("Disabling interactive mode in CI")
37
+ return False
38
+ return self._interactive
39
+
40
+ @interactive.setter
41
+ def interactive(self, value:bool):
42
+ self._interactive = value
43
+
19
44
 
20
45
 
21
46
  config = MPtoolConfig()
47
+ __version__ = get_version()
@@ -21,6 +21,7 @@ from rich.progress import track
21
21
  from mpflash.common import PORT_FWTYPES, FWInfo
22
22
  from mpflash.errors import MPFlashError
23
23
  from mpflash.mpboard_id import get_known_ports
24
+ from mpflash.vendor.versions import clean_version
24
25
 
25
26
  # avoid conflict with the ujson used by MicroPython
26
27
  jsonlines.ujson = None # type: ignore
@@ -32,8 +33,18 @@ MICROPYTHON_ORG_URL = "https://micropython.org/"
32
33
  # Regexes to remove dates and hashes in the filename that just get in the way
33
34
  RE_DATE = r"(-\d{8}-)"
34
35
  RE_HASH = r"(.g[0-9a-f]+\.)"
35
- # regex to extract the version from the firmware filename
36
- RE_VERSION_PREVIEW = r"(\d+\.\d+(\.\d+)?(-\w+.\d+)?)"
36
+ # regex to extract the version and the build from the firmware filename
37
+ # group 1 is the version, group 2 is the build
38
+ RE_VERSION_PREVIEW = r"v([\d\.]+)-?(?:preview\.)?(\d+)?\."
39
+ # 'RPI_PICO_W-v1.23.uf2'
40
+ # 'RPI_PICO_W-v1.23.0.uf2'
41
+ # 'RPI_PICO_W-v1.23.0-406.uf2'
42
+ # 'RPI_PICO_W-v1.23.0-preview.406.uf2'
43
+ # 'RPI_PICO_W-v1.23.0-preview.4.uf2'
44
+ # 'RPI_PICO_W-v1.23.0.uf2'
45
+ # 'https://micropython.org/resources/firmware/RPI_PICO_W-20240531-v1.24.0-preview.10.gc1a6b95bf.uf2'
46
+ # 'https://micropython.org/resources/firmware/RPI_PICO_W-20240531-v1.24.0-preview.10.uf2'
47
+ # 'RPI_PICO_W-v1.24.0-preview.10.gc1a6b95bf.uf2'
37
48
 
38
49
 
39
50
  # use functools.lru_cache to avoid needing to download pages multiple times
@@ -98,6 +109,7 @@ def board_firmware_urls(board_url: str, base_url: str, ext: str) -> List[str]:
98
109
  # The first run takes ~60 seconds to run for 4 ports , all boards
99
110
  # so it makes sense to cache the results and skip boards as soon as possible
100
111
  def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]:
112
+ # sourcery skip: use-getitem-for-re-match-groups
101
113
  """
102
114
  Retrieves a list of firmware information for the specified ports and boards.
103
115
 
@@ -146,13 +158,15 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]
146
158
  # board["firmware"] = _url
147
159
  # board["preview"] = "preview" in _url # type: ignore
148
160
  if ver_match := re.search(RE_VERSION_PREVIEW, _url):
149
- fw_info.version = ver_match[1]
161
+ fw_info.version = clean_version(ver_match.group(1))
162
+ fw_info.build = ver_match.group(2) or "0"
163
+ fw_info.preview = fw_info.build != "0"
164
+ # # else:
165
+ # # board.$1= ""
166
+ # if "preview." in fw_info.version:
167
+ # fw_info.build = fw_info.version.split("preview.")[-1]
150
168
  # 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"
169
+ # fw_info.build = "0"
156
170
 
157
171
  fw_info.ext = Path(fw_info.firmware).suffix
158
172
  fw_info.variant = fw_info.filename.split("-v")[0] if "-v" in fw_info.filename else ""
@@ -180,9 +194,21 @@ def download_firmwares(
180
194
  force: bool = False,
181
195
  clean: bool = True,
182
196
  ) -> int:
197
+ """
198
+ Downloads firmware files based on the specified firmware folder, ports, boards, versions, force flag, and clean flag.
199
+
200
+ Args:
201
+ firmware_folder : The folder to save the downloaded firmware files.
202
+ ports : The list of ports to check for firmware.
203
+ boards : The list of boards to download firmware for.
204
+ versions : The list of versions to download firmware for.
205
+ force : A flag indicating whether to force the download even if the firmware file already exists.
206
+ clean : A flag indicating to clean the date from the firmware filename.
207
+ """
183
208
  skipped = downloaded = 0
184
- if versions is None:
185
- versions = []
209
+ versions = [] if versions is None else [clean_version(v) for v in versions]
210
+ # handle renamed boards
211
+ boards = add_renamed_boards(boards)
186
212
 
187
213
  unique_boards = get_firmware_list(ports, boards, versions, clean)
188
214
 
@@ -191,6 +217,11 @@ def download_firmwares(
191
217
  # relevant
192
218
 
193
219
  log.info(f"Found {len(unique_boards)} relevant unique firmwares")
220
+ if not unique_boards:
221
+ log.error("No relevant firmwares could be found on https://micropython.org/download")
222
+ log.info(f"{versions=} {ports=} {boards=}")
223
+ log.info("Please check the website for the latest firmware files or try the preview version.")
224
+ return 0
194
225
 
195
226
  firmware_folder.mkdir(exist_ok=True)
196
227
 
@@ -214,7 +245,9 @@ def download_firmwares(
214
245
  continue
215
246
  writer.write(board.to_dict())
216
247
  downloaded += 1
217
- log.info(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
248
+ # if downloaded > 0:
249
+ # clean_downloaded_firmwares(firmware_folder)
250
+ log.success(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
218
251
  return downloaded + skipped
219
252
 
220
253
 
@@ -238,12 +271,15 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
238
271
  board_urls = sorted(get_boards(ports, boards, clean), key=key_fw_ver_pre_ext_bld)
239
272
 
240
273
  log.debug(f"Total {len(board_urls)} firmwares")
274
+
241
275
  relevant = [
242
276
  board
243
277
  for board in board_urls
244
- if board.board in boards and (board.version in versions or board.preview and preview)
245
- # and b["port"] in ["esp32", "rp2"]
278
+ if board.version in versions and board.build == "0" and board.board in boards and not board.preview
246
279
  ]
280
+
281
+ if preview:
282
+ relevant.extend([board for board in board_urls if board.board in boards and board.preview])
247
283
  log.debug(f"Matching firmwares: {len(relevant)}")
248
284
  # select the unique boards
249
285
  unique_boards: List[FWInfo] = []
@@ -272,7 +308,7 @@ def download(
272
308
  boards : The list of boards to download firmware for.
273
309
  versions : The list of versions to download firmware for.
274
310
  force : A flag indicating whether to force the download even if the firmware file already exists.
275
- clean : A flag indicating whether to perform a clean download.
311
+ clean : A flag indicating whether to clean the date from the firmware filename.
276
312
 
277
313
  Returns:
278
314
  int: The number of downloaded firmware files.
@@ -284,11 +320,41 @@ def download(
284
320
  if not boards:
285
321
  log.critical("No boards found, please connect a board or specify boards to download firmware for.")
286
322
  raise MPFlashError("No boards found")
287
- # versions = [clean_version(v, drop_v=True) for v in versions] # remove leading v from version
323
+
288
324
  try:
289
325
  destination.mkdir(exist_ok=True, parents=True)
290
326
  except (PermissionError, FileNotFoundError) as e:
291
327
  log.critical(f"Could not create folder {destination}")
292
328
  raise MPFlashError(f"Could not create folder {destination}") from e
329
+ try:
330
+ result = download_firmwares(destination, ports, boards, versions, force=force, clean=clean)
331
+ except requests.exceptions.RequestException as e:
332
+ log.exception(e)
333
+ raise MPFlashError("Could not connect to micropython.org") from e
334
+
335
+ return result
336
+
337
+
338
+ def add_renamed_boards(boards: List[str]) -> List[str]:
339
+ """
340
+ Adds the renamed boards to the list of boards.
293
341
 
294
- return download_firmwares(destination, ports, boards, versions, force=force, clean=clean)
342
+ Args:
343
+ boards : The list of boards to add the renamed boards to.
344
+
345
+ Returns:
346
+ List[str]: The list of boards with the renamed boards added.
347
+
348
+ """
349
+ renamed = {
350
+ "PICO": ["RPI_PICO"],
351
+ "PICO_W": ["RPI_PICO_W"],
352
+ "GENERIC": ["ESP32_GENERIC", "ESP8266_GENERIC"], # just add both of them
353
+ }
354
+ _boards = boards.copy()
355
+ for board in boards:
356
+ if board in renamed and renamed[board] not in boards:
357
+ _boards.extend(renamed[board])
358
+ if board != board.upper() and board.upper() not in boards:
359
+ _boards.append(board.upper())
360
+ return _boards
@@ -24,10 +24,26 @@ def downloaded_firmwares(fw_folder: Path) -> List[FWInfo]:
24
24
  return firmwares
25
25
 
26
26
 
27
+ def clean_downloaded_firmwares(fw_folder: Path) -> None:
28
+ """
29
+ Remove duplicate entries from the firmware.jsonl file, keeping the latest one
30
+ uniqueness is based on the filename
31
+ """
32
+ firmwares = downloaded_firmwares(fw_folder)
33
+ if not firmwares:
34
+ return
35
+ # keep the latest entry
36
+ unique_fw = {fw.filename: fw for fw in firmwares}
37
+ with jsonlines.open(fw_folder / "firmware.jsonl", "w") as writer:
38
+ for fw in unique_fw.values():
39
+ writer.write(fw.to_dict())
40
+ log.info(f"Removed duplicate entries from firmware.jsonl in {fw_folder}")
41
+
42
+
27
43
  def find_downloaded_firmware(
28
44
  *,
29
45
  board_id: str,
30
- version: str = "",
46
+ version: str = "", # v1.2.3
31
47
  port: str = "",
32
48
  variants: bool = False,
33
49
  fw_folder: Optional[Path] = None,
@@ -38,21 +54,22 @@ def find_downloaded_firmware(
38
54
  selector = {}
39
55
  fw_folder = fw_folder or config.firmware_folder
40
56
  # Use the information in firmwares.jsonl to find the firmware file
57
+ log.debug(f"{trie}] Looking for firmware for {board_id} {version} ")
41
58
  fw_list = downloaded_firmwares(fw_folder)
42
59
  if not fw_list:
43
60
  log.error("No firmware files found. Please download the firmware first.")
44
61
  return []
45
62
  # filter by version
46
- version = clean_version(version, drop_v=True)
63
+ version = clean_version(version)
47
64
  fw_list = filter_downloaded_fwlist(fw_list, board_id, version, port, variants, selector)
48
65
 
49
66
  if not fw_list and trie < 3:
50
67
  log.info(f"Try ({trie+1}) to find a firmware for the board {board_id}")
51
68
  if trie == 1:
52
- # ESP board naming conventions have changed by adding a PORT refix
69
+ # ESP board naming conventions have changed by adding a PORT prefix
53
70
  if port.startswith("esp") and not board_id.startswith(port.upper()):
54
71
  board_id = f"{port.upper()}_{board_id}"
55
- # RP2 board naming conventions have changed by adding a _RPIprefix
72
+ # RP2 board naming conventions have changed by adding a _RPI prefix
56
73
  if port == "rp2" and not board_id.startswith("RPI_"):
57
74
  board_id = f"RPI_{board_id}"
58
75
  elif trie == 2:
@@ -75,7 +92,7 @@ def find_downloaded_firmware(
75
92
  def filter_downloaded_fwlist(
76
93
  fw_list: List[FWInfo],
77
94
  board_id: str,
78
- version: str,
95
+ version: str, # v1.2.3
79
96
  port: str,
80
97
  # preview: bool,
81
98
  variants: bool,
@@ -86,11 +103,14 @@ def filter_downloaded_fwlist(
86
103
  # never get a preview for an older version
87
104
  fw_list = [fw for fw in fw_list if fw.preview]
88
105
  else:
89
- fw_list = [fw for fw in fw_list if fw.version == version]
90
-
106
+ # older FWInfo version has no v1.2.3 prefix
107
+ either = [clean_version(version, drop_v=False), clean_version(version, drop_v=True)]
108
+ fw_list = [fw for fw in fw_list if fw.version in either]
109
+ log.trace(f"Filtering firmware for {version} : {len(fw_list)} found.")
91
110
  # filter by port
92
111
  if port:
93
112
  fw_list = [fw for fw in fw_list if fw.port == port]
113
+ log.trace(f"Filtering firmware for {port} : {len(fw_list)} found.")
94
114
 
95
115
  if board_id:
96
116
  if variants:
@@ -99,6 +119,7 @@ def filter_downloaded_fwlist(
99
119
  else:
100
120
  # the firmware variant should match exactly the board_id
101
121
  fw_list = [fw for fw in fw_list if fw.variant == board_id]
122
+ log.trace(f"Filtering firmware for {board_id} : {len(fw_list)} found.")
102
123
  if selector and port in selector:
103
124
  fw_list = [fw for fw in fw_list if fw.filename.endswith(selector[port])]
104
125
  return fw_list
mpflash/mpflash/errors.py CHANGED
@@ -2,4 +2,8 @@
2
2
 
3
3
 
4
4
  class MPFlashError(Exception):
5
- pass
5
+ """Base class for exceptions in this module."""
6
+
7
+ def __init__(self, message: str):
8
+ self.message = message
9
+ super().__init__(message)
mpflash/mpflash/flash.py CHANGED
@@ -1,10 +1,9 @@
1
- import time
2
1
  from pathlib import Path
3
2
 
4
3
  from loguru import logger as log
5
4
 
6
- from mpflash.common import PORT_FWTYPES
7
- from mpflash.mpremoteboard import MPRemoteBoard
5
+ from mpflash.bootloader import enter_bootloader
6
+ from mpflash.common import PORT_FWTYPES, BootloaderMethod
8
7
 
9
8
  from .flash_esp import flash_esp
10
9
  from .flash_stm32 import flash_stm32
@@ -14,13 +13,15 @@ from .worklist import WorkList
14
13
  # #########################################################################################################
15
14
 
16
15
 
16
+
17
17
  def flash_list(
18
18
  todo: WorkList,
19
19
  fw_folder: Path,
20
20
  erase: bool,
21
- bootloader: bool,
21
+ bootloader: BootloaderMethod,
22
22
  ):
23
23
  """Flash a list of boards with the specified firmware."""
24
+ UF2_PORTS = [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts]
24
25
  flashed = []
25
26
  for mcu, fw_info in todo:
26
27
  fw_file = fw_folder / fw_info.filename
@@ -30,14 +31,13 @@ def flash_list(
30
31
  log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info.version}")
31
32
  updated = None
32
33
  # try:
33
- if mcu.port in [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts] and fw_file.suffix == ".uf2":
34
- # any .uf2 port ["samd", "rp2", "nrf"]:
35
- if bootloader:
36
- enter_bootloader(mcu)
34
+ if mcu.port in UF2_PORTS and fw_file.suffix == ".uf2":
35
+ if not enter_bootloader(mcu, bootloader):
36
+ continue
37
37
  updated = flash_uf2(mcu, fw_file=fw_file, erase=erase)
38
38
  elif mcu.port in ["stm32"]:
39
- if bootloader:
40
- enter_bootloader(mcu)
39
+ if not enter_bootloader(mcu, bootloader):
40
+ continue
41
41
  updated = flash_stm32(mcu, fw_file, erase=erase)
42
42
  elif mcu.port in ["esp32", "esp8266"]:
43
43
  # bootloader is handled by esptool for esp32/esp8266
@@ -50,20 +50,3 @@ def flash_list(
50
50
  else:
51
51
  log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
52
52
  return flashed
53
-
54
-
55
- def enter_bootloader(mcu: MPRemoteBoard, timeout: int = 10, wait_after: int = 2):
56
- """Enter the bootloader mode for the board"""
57
- log.info(f"Entering bootloader on {mcu.board} on {mcu.serialport}")
58
- mcu.run_command("bootloader", timeout=timeout)
59
- time.sleep(wait_after)
60
-
61
-
62
- # TODO:
63
- # flash from some sort of queue to allow different images to be flashed to the same board
64
- # - flash variant 1
65
- # - stub variant 1
66
- # - flash variant 2
67
- # - stub variant 2
68
- #
69
- # JIT download / download any missing firmwares based on the detected boards
@@ -14,7 +14,7 @@ from rich.progress import track
14
14
  from mpflash.mpremoteboard import MPRemoteBoard
15
15
 
16
16
  from .common import PORT_FWTYPES
17
- from .config import config
17
+ from .flash_uf2_boardid import get_board_id
18
18
  from .flash_uf2_linux import dismount_uf2_linux, wait_for_UF2_linux
19
19
  from .flash_uf2_macos import wait_for_UF2_macos
20
20
  from .flash_uf2_windows import wait_for_UF2_windows
@@ -45,11 +45,7 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
45
45
  destination = wait_for_UF2_windows()
46
46
  elif sys.platform == "darwin":
47
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()
48
+ destination = wait_for_UF2_macos()
53
49
  else:
54
50
  log.warning(f"OS {sys.platform} not tested/supported")
55
51
  return None
@@ -59,6 +55,8 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
59
55
  return None
60
56
 
61
57
  log.info("Board is in bootloader mode")
58
+ board_id = get_board_id(destination) # type: ignore
59
+ log.info(f"Board ID: {board_id}")
62
60
  log.info(f"Copying {fw_file} to {destination}.")
63
61
  shutil.copy(fw_file, destination)
64
62
  log.success("Done copying, resetting the board and wait for it to restart")
@@ -1,4 +1,5 @@
1
1
  from pathlib import Path
2
+
2
3
  from loguru import logger as log
3
4
 
4
5
 
@@ -10,5 +11,5 @@ def get_board_id(path: Path):
10
11
  for line in data:
11
12
  if line.startswith("Board-ID"):
12
13
  board_id = line[9:].strip()
13
- log.trace(f"Found Board-ID={board_id}")
14
+ log.debug(f"INFO_UF2.TXT Board-ID={board_id}")
14
15
  return board_id
@@ -3,76 +3,32 @@
3
3
  # sourcery skip: snake-case-functions
4
4
  from __future__ import annotations
5
5
 
6
- import sys
7
6
  import time
8
7
  from pathlib import Path
8
+ from typing import Optional
9
9
 
10
- from loguru import logger as log
11
10
  from rich.progress import track
12
11
 
13
- from .flash_uf2_boardid import get_board_id
14
- from .uf2disk import UF2Disk
15
12
 
16
13
 
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:
14
+ def wait_for_UF2_macos(s_max: int = 10) -> Optional[Path]:
15
+ """Wait for the MCU to mount as a drive"""
16
+ if s_max < 1:
17
+ s_max = 10
18
+ destination = None
59
19
  for _ in track(
60
20
  range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
61
21
  ):
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)
22
+ # log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
23
+ vol_mounts = Path("/Volumes").iterdir()
24
+ for vol in vol_mounts:
66
25
  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)
26
+ if Path(vol, "INFO_UF2.TXT").exists():
27
+ destination = Path(vol)
70
28
  break
71
- except PermissionError:
72
- log.debug(f"Permission error on {drive.mountpoint}")
73
- continue
29
+ except OSError:
30
+ pass
74
31
  if destination:
75
32
  break
76
33
  time.sleep(1)
77
- wait -= 1
78
34
  return destination
@@ -5,25 +5,25 @@ from __future__ import annotations
5
5
 
6
6
  import time
7
7
  from pathlib import Path
8
+ from typing import Optional
8
9
 
9
10
  import psutil
10
11
  from rich.progress import track
11
12
 
12
- from .flash_uf2_boardid import get_board_id
13
13
 
14
14
 
15
- def wait_for_UF2_windows(s_max: int = 10):
15
+
16
+ def wait_for_UF2_windows(s_max: int = 10) -> Optional[Path]:
16
17
  """Wait for the MCU to mount as a drive"""
17
18
  if s_max < 1:
18
19
  s_max = 10
19
- destination = ""
20
+ destination = None
20
21
  for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True,refresh_per_second=2):
21
22
  # log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
22
23
  drives = [drive.device for drive in psutil.disk_partitions()]
23
24
  for drive in drives:
24
25
  try:
25
26
  if Path(drive, "INFO_UF2.TXT").exists():
26
- board_id = get_board_id(Path(drive)) # type: ignore
27
27
  destination = Path(drive)
28
28
  break
29
29
  except OSError:
@@ -77,10 +77,14 @@ def known_stored_boards(port: str, versions: Optional[List[str]] = None) -> List
77
77
  @lru_cache(maxsize=20)
78
78
  def find_known_board(board_id: str) -> Board:
79
79
  """Find the board for the given BOARD_ID or 'board description' and return the board info as a Board object"""
80
+ # FIXME : functional overlap with:
81
+ # mpboard_id\board_id.py _find_board_id_by_description
80
82
  info = read_known_boardinfo()
81
83
  for board_info in info:
82
84
  if board_id in (board_info.board_id, board_info.description):
83
85
  if not board_info.cpu:
86
+ # safeguard for older board_info.json files
87
+ print(f"Board {board_id} has no CPU info, using port as CPU")
84
88
  if " with " in board_info.description:
85
89
  board_info.cpu = board_info.description.split(" with ")[-1]
86
90
  else: