mpflash 0.7.6__py3-none-any.whl → 0.8.0__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.
mpflash/flash_esp.py CHANGED
@@ -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_known_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:
mpflash/flash_uf2.py CHANGED
@@ -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()
65
+ if sys.platform in ["linux"]:
66
+ dismount_uf2_linux()
59
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,refresh_per_second=2):
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
mpflash/list.py CHANGED
@@ -1,52 +1,13 @@
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
-
17
-
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
-
29
- # a lot of boilerplate to show a progress bar with the comport currently scanned
30
- # low update rate to facilitate screen readers/narration
31
- with Progress(rp_spinner, rp_text, rp_bar, TimeElapsedColumn(), refresh_per_second=2) as progress:
32
- tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
33
- progress.tasks[tsk_scan].fields["device"] = "..."
34
- progress.tasks[tsk_scan].visible = True
35
- progress.start_task(tsk_scan)
36
- try:
37
- for mcu in conn_mcus:
38
- progress.update(tsk_scan, device=mcu.serialport.replace("/dev/", ""))
39
- try:
40
- mcu.get_mcu_info()
41
- except ConnectionError as e:
42
- print(f"Error: {e}")
43
- continue
44
- finally:
45
- # transient
46
- progress.stop_task(tsk_scan)
47
- progress.tasks[tsk_scan].visible = False
48
- return conn_mcus
49
-
50
11
 
51
12
  def show_mcus(
52
13
  conn_mcus: List[MPRemoteBoard],
@@ -57,11 +18,9 @@ def show_mcus(
57
18
 
58
19
 
59
20
  def abbrv_family(family: str, is_wide: bool) -> str:
60
- ABRV = {"micropython": "upy", "circuitpython": "cpy"}
61
21
  if not is_wide:
62
- if family in ABRV:
63
- return ABRV[family]
64
- return family[:4]
22
+ ABRV = {"micropython": "upy", "circuitpython": "cpy", "unknown": "?"}
23
+ return ABRV.get(family, family[:4])
65
24
  return family
66
25
 
67
26
 
@@ -4,56 +4,38 @@ that is included in the module.
4
4
 
5
5
  """
6
6
 
7
- import json
8
7
  from functools import lru_cache
9
- from pathlib import Path
10
- from typing import List, Optional, Tuple, TypedDict, Union
8
+ from typing import List, Optional, Tuple
11
9
 
12
- from mpflash.common import PORT_FWTYPES
13
10
  from mpflash.errors import MPFlashError
11
+ from mpflash.mpboard_id.board import Board
12
+ from mpflash.mpboard_id.store import read_known_boardinfo
14
13
  from mpflash.vendor.versions import clean_version
15
14
 
16
15
  # KNOWN ports and boards are sourced from the micropython repo,
17
16
  # this info is stored in the board_info.json file
18
17
 
19
18
 
20
- # Board based on the dataclass Board but changed to TypedDict
21
- # - source : get_boardnames.py
22
- class Board(TypedDict):
23
- """MicroPython Board definition"""
24
-
25
- description: str
26
- port: str
27
- board: str
28
- board_name: str
29
- mcu_name: str
30
- path: Union[Path, str]
31
- version: str
32
- cpu: str
33
-
34
-
35
- @lru_cache(maxsize=None)
36
- def read_known_boardinfo() -> List[Board]:
37
- """Reads the board_info.json file and returns the data as a list of Board objects"""
38
- with open(Path(__file__).parent / "board_info.json", "r") as file:
39
- return json.load(file)
40
-
41
-
42
19
  def get_known_ports() -> List[str]:
43
20
  # TODO: Filter for Version
44
21
  mp_boards = read_known_boardinfo()
45
22
  # select the unique ports from info
46
- ports = set({board["port"] for board in mp_boards if board["port"] in PORT_FWTYPES.keys()})
23
+ ports = set({board.port for board in mp_boards if board.port})
47
24
  return sorted(list(ports))
48
25
 
49
26
 
50
- def get_known_boards_for_port(port: str, versions: Optional[List[str]] = None):
27
+ def get_known_boards_for_port(port: Optional[str] = "", versions: Optional[List[str]] = None) -> List[Board]:
51
28
  """
52
29
  Returns a list of boards for the given port and version(s)
53
30
 
54
- port : str : The Micropython port to filter for
55
- versions : List[str] : The Micropython versions to filter for (actual versions required)"""
31
+ port: The Micropython port to filter for
32
+ versions: Optional, The Micropython versions to filter for (actual versions required)
33
+ """
56
34
  mp_boards = read_known_boardinfo()
35
+ if versions:
36
+ preview_or_stable = "preview" in versions or "stable" in versions
37
+ else:
38
+ preview_or_stable = False
57
39
 
58
40
  # filter for 'preview' as they are not in the board_info.json
59
41
  # instead use stable version
@@ -65,9 +47,17 @@ def get_known_boards_for_port(port: str, versions: Optional[List[str]] = None):
65
47
  # make sure of the v prefix
66
48
  versions = [clean_version(v) for v in versions]
67
49
  # filter for the version(s)
68
- mp_boards = [board for board in mp_boards if board["version"] in versions]
50
+ mp_boards = [board for board in mp_boards if board.version in versions]
51
+ if not mp_boards and preview_or_stable:
52
+ # nothing found - perhaps there is a newer version for which we do not have the board info yet
53
+ # use the latest known version from the board info
54
+ mp_boards = read_known_boardinfo()
55
+ last_known_version = sorted({b.version for b in mp_boards})[-1]
56
+ mp_boards = [board for board in mp_boards if board.version == last_known_version]
57
+
69
58
  # filter for the port
70
- mp_boards = [board for board in mp_boards if board["port"] == port]
59
+ if port:
60
+ mp_boards = [board for board in mp_boards if board.port == port]
71
61
  return mp_boards
72
62
 
73
63
 
@@ -80,7 +70,7 @@ def known_stored_boards(port: str, versions: Optional[List[str]] = None) -> List
80
70
  """
81
71
  mp_boards = get_known_boards_for_port(port, versions)
82
72
 
83
- boards = set({(f'{board["version"]} {board["description"]}', board["board"]) for board in mp_boards})
73
+ boards = set({(f"{board.version} {board.description}", board.board_id) for board in mp_boards})
84
74
  return sorted(list(boards))
85
75
 
86
76
 
@@ -89,11 +79,11 @@ def find_known_board(board_id: str) -> Board:
89
79
  """Find the board for the given BOARD_ID or 'board description' and return the board info as a Board object"""
90
80
  info = read_known_boardinfo()
91
81
  for board_info in info:
92
- if board_id in (board_info["board"], board_info["description"]):
93
- if "cpu" not in board_info or not board_info["cpu"]:
94
- if " with " in board_info["description"]:
95
- board_info["cpu"] = board_info["description"].split(" with ")[-1]
82
+ if board_id in (board_info.board_id, board_info.description):
83
+ if not board_info.cpu:
84
+ if " with " in board_info.description:
85
+ board_info.cpu = board_info.description.split(" with ")[-1]
96
86
  else:
97
- board_info["cpu"] = board_info["port"]
87
+ board_info.cpu = board_info.port
98
88
  return board_info
99
89
  raise MPFlashError(f"Board {board_id} not found")
@@ -0,0 +1,255 @@
1
+ """
2
+ Collects board name and description information from MicroPython and writes it to JSON and CSV files.
3
+ """
4
+
5
+ import re
6
+ from pathlib import Path
7
+ from typing import List, Optional
8
+
9
+ import inquirer
10
+ import rich
11
+ import rich.table
12
+ from rich.console import Console
13
+ from rich.progress import track
14
+
15
+ import mpflash.vendor.basicgit as git
16
+ from mpflash.logger import log
17
+ from mpflash.mpboard_id import Board
18
+ from mpflash.mpboard_id.store import write_boardinfo_json
19
+ from mpflash.vendor.versions import micropython_versions
20
+
21
+ # look for all mpconfigboard.h files and extract the board name
22
+ # from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6"
23
+ # and the #define MICROPY_HW_MCU_NAME "STM32F767xx"
24
+ RE_H_MICROPY_HW_BOARD_NAME = re.compile(r"#define\s+MICROPY_HW_BOARD_NAME\s+\"(.+)\"")
25
+ RE_H_MICROPY_HW_MCU_NAME = re.compile(r"#define\s+MICROPY_HW_MCU_NAME\s+\"(.+)\"")
26
+ # find in the mpconfigboard.cmake files
27
+
28
+ RE_CMAKE_MICROPY_HW_BOARD_NAME = re.compile(r"MICROPY_HW_BOARD_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
29
+ RE_CMAKE_MICROPY_HW_MCU_NAME = re.compile(r"MICROPY_HW_MCU_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
30
+ # TODO: normal make files
31
+
32
+
33
+ def boards_from_repo(mpy_path: Path, version: str, family: Optional[str] = None) -> List[Board]:
34
+ """Collects board name and decriptions from mpconfigboard.h files.
35
+
36
+ Args:
37
+ mpy_path (Path): The path to the MicroPython repository.
38
+ version (str): The version of MicroPython.
39
+
40
+ Returns:
41
+ List[Board]: A list of Board objects containing the board information.
42
+ """
43
+ if not mpy_path.exists() or not mpy_path.is_dir():
44
+ raise FileNotFoundError(f"MicroPython path {mpy_path} does not exist.")
45
+ if not family:
46
+ family = "micropython"
47
+ if not version:
48
+ version = git.get_local_tag() # type: ignore
49
+ if not version:
50
+ raise ValueError("No version provided and no local tag found.")
51
+
52
+ board_list: List[Board] = []
53
+ # look in mpconfigboard.h files
54
+ board_list = boards_from_cmake(mpy_path, version, family)
55
+
56
+ # look for variants in the .cmake files
57
+ board_list.extend(boards_from_headers(mpy_path, version, family))
58
+ # TODO:? look for variants in the Makefile files
59
+
60
+ return board_list
61
+
62
+
63
+ def boards_from_cmake(mpy_path: Path, version: str, family: str):
64
+ """Get boards from the mpconfigboard.cmake files to the board_list."""
65
+ board_list = []
66
+ for path in mpy_path.glob("ports/**/mpconfigboard.cmake"):
67
+ board = path.parent.name
68
+ port = path.parent.parent.parent.name
69
+ with open(path, "r") as f:
70
+ board_name = mcu_name = "-"
71
+ for line in f:
72
+ line = line.strip()
73
+ if match := RE_CMAKE_MICROPY_HW_BOARD_NAME.match(line):
74
+ description = match["variant"]
75
+ board_list.append(
76
+ Board(
77
+ board_id=board,
78
+ port=port,
79
+ board_name=board_name,
80
+ mcu_name=mcu_name,
81
+ description=description,
82
+ path=path.relative_to(mpy_path),
83
+ version=version,
84
+ family=family,
85
+ )
86
+ )
87
+ elif match := RE_CMAKE_MICROPY_HW_MCU_NAME.match(line):
88
+ description = match["variant"]
89
+ board_list.append(
90
+ Board(
91
+ board_id=board,
92
+ port=port,
93
+ board_name=board_name,
94
+ mcu_name=mcu_name,
95
+ description=description,
96
+ path=path.relative_to(mpy_path),
97
+ version=version,
98
+ family=family,
99
+ )
100
+ )
101
+ return board_list
102
+
103
+
104
+ def boards_from_headers(mpy_path: Path, version: str, family: str):
105
+ """Get boards from the mpconfigboard.h files to the board_list."""
106
+ board_list = []
107
+ for path in mpy_path.glob("ports/**/mpconfigboard.h"):
108
+ board = path.parent.name
109
+ port = path.parent.parent.parent.name
110
+ with open(path, "r") as f:
111
+ board_name = mcu_name = "-"
112
+ found = 0
113
+ for line in f:
114
+ if match := RE_H_MICROPY_HW_BOARD_NAME.match(line):
115
+ board_name = match[1]
116
+ found += 1
117
+ elif match := RE_H_MICROPY_HW_MCU_NAME.match(line):
118
+ mcu_name = match[1]
119
+ found += 1
120
+ if found == 2:
121
+ description = f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name
122
+ board_list.append(
123
+ Board(
124
+ board_id=board,
125
+ port=port,
126
+ board_name=board_name,
127
+ mcu_name=mcu_name,
128
+ description=description,
129
+ path=path.relative_to(mpy_path),
130
+ version=version,
131
+ family=family,
132
+ )
133
+ )
134
+ found = 0
135
+ if found == 1:
136
+ description = board_name
137
+ board_list.append(
138
+ Board(
139
+ board_id=board,
140
+ port=port,
141
+ board_name=board_name,
142
+ mcu_name=mcu_name,
143
+ description=description,
144
+ path=path.relative_to(mpy_path),
145
+ version=version,
146
+ family=family,
147
+ )
148
+ )
149
+ return board_list
150
+
151
+
152
+ def boards_for_versions(versions: List[str], mpy_path: Path):
153
+ """Gets the list of boards for multiple versions of MicroPython.
154
+
155
+ Args:
156
+ versions (List[str]): The list of MicroPython versions.
157
+ mpy_path (Path): The path to the MicroPython repository.
158
+
159
+ Returns:
160
+ List[Board]: The list of Board objects.
161
+ """
162
+ board_list: List[Board] = []
163
+ for version in track(versions, description="Searching MicroPython versions"):
164
+ if git.checkout_tag(tag=version, repo=mpy_path):
165
+ new_ones = boards_from_repo(mpy_path, version, family="micropython")
166
+ print(f"Found {len(new_ones)} board definitions for {version}.")
167
+ board_list += new_ones
168
+ else:
169
+ print(f"Could not checkout version {version}.")
170
+
171
+ # sort the board_list by description and board
172
+ print("Total number of boards found:", len(board_list))
173
+
174
+ board_list = unique_boards(board_list)
175
+ print("Unique board descriptions found:", len(board_list))
176
+ return board_list
177
+
178
+
179
+ def unique_boards(board_list: List[Board], *, key_version: bool = True):
180
+ """Remove duplicate boards by 'BOARD_ID description' from the list."""
181
+ seen = set()
182
+ result = []
183
+ for x in board_list:
184
+ if key_version:
185
+ key = f"{x.board_id}|{x.version}|{x.description}"
186
+ else:
187
+ key = f"{x.board_id}|{x.description}"
188
+ if key not in seen:
189
+ result.append(x)
190
+ seen.add(key)
191
+ result.sort(key=lambda x: x.description.lower())
192
+ return result
193
+
194
+
195
+ def make_table(board_list: List[Board]) -> rich.table.Table:
196
+ """Creates a rich table with board information."""
197
+ is_wide = True
198
+
199
+ table = rich.table.Table(title="MicroPython Board Information")
200
+ table.add_column("BOARD_ID", justify="left", style="green")
201
+ table.add_column("Description", justify="left", style="cyan")
202
+ table.add_column("Port", justify="left", style="magenta")
203
+ table.add_column("Board Name", justify="left", style="blue")
204
+ if is_wide:
205
+ table.add_column("MCU Name", justify="left", style="blue")
206
+ table.add_column("Detection", justify="left", style="yellow")
207
+ table.add_column("Version", justify="left", style="blue")
208
+ if is_wide:
209
+ table.add_column("Family", justify="left", style="blue")
210
+
211
+ for board in board_list:
212
+ row = [board.board_id, board.description, *(board.port, board.board_name)]
213
+ if is_wide:
214
+ row.append(board.mcu_name)
215
+ row.extend((str(Path(board.path).suffix), board.version))
216
+ if is_wide:
217
+ row.append(board.family)
218
+ table.add_row(*row)
219
+
220
+ return table
221
+
222
+
223
+ def ask_mpy_path():
224
+ """Ask the user for the path to the MicroPython repository."""
225
+ questions = [
226
+ inquirer.Text(
227
+ "mpy_path", message="Enter the path to the MicroPython repository", default=".\\repos\\micropython"
228
+ )
229
+ ]
230
+ if answers := inquirer.prompt(questions):
231
+ return Path(answers["mpy_path"])
232
+ else:
233
+ raise ValueError("No path provided.")
234
+
235
+
236
+ def main():
237
+ """Main function to collect and write board information."""
238
+
239
+ console = Console()
240
+
241
+ mpy_path = ask_mpy_path()
242
+ versions = micropython_versions(minver="v1.10") + ["master"]
243
+ board_list = boards_for_versions(versions, mpy_path)
244
+
245
+ here = Path(__file__).parent
246
+ log.info(write_boardinfo_json(board_list, folder=here))
247
+ # write_files(board_list, folder=CONFIG.board_path)
248
+
249
+ # table of when the board was added
250
+ table = make_table(unique_boards(board_list, key_version=False))
251
+ console.print(table)
252
+
253
+
254
+ if __name__ == "__main__":
255
+ main()
@@ -0,0 +1,37 @@
1
+ from dataclasses import dataclass, field
2
+ from pathlib import Path
3
+ from typing import Union
4
+
5
+
6
+ # - source : get_boardnames.py
7
+ @dataclass
8
+ class Board:
9
+ """
10
+ MicroPython Board definitions, parsed from the make and header files
11
+ """
12
+
13
+ port: str # micropython port
14
+ board_id: str # BOARD_ID (Foldername) as used in the make files
15
+ board_name: str # Short board description
16
+ description: str # Long board description
17
+ path: Union[Path, str]
18
+ version: str = field(default="") # version of MicroPython""
19
+ # versions: List[str] = field(default=[]) # version of MicroPython""
20
+ family: str = field(default="micropython")
21
+ mcu_name: str = field(default="")
22
+ cpu: str = field(default="")
23
+ # TODO: add variant
24
+
25
+ def __post_init__(self):
26
+ if not self.cpu:
27
+ if " with " in self.description:
28
+ self.cpu = self.description.split(" with ")[-1]
29
+ else:
30
+ self.cpu = self.port
31
+
32
+ @staticmethod
33
+ def from_dict(data: dict) -> "Board":
34
+ return Board(**data)
35
+
36
+ def to_dict(self) -> dict:
37
+ return self.__dict__