mpflash 0.7.7__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_uf2.py CHANGED
@@ -15,7 +15,7 @@ from mpflash.mpremoteboard import MPRemoteBoard
15
15
 
16
16
  from .common import PORT_FWTYPES
17
17
  from .config import config
18
- from .flash_uf2_linux import dismount_uf2, wait_for_UF2_linux
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
21
21
 
@@ -29,9 +29,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
29
29
  - copy the firmware file to the drive
30
30
  - wait for the device to restart (5s)
31
31
 
32
- for Lunix :
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.
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.
35
35
  """
36
36
  if ".uf2" not in PORT_FWTYPES[mcu.port]:
37
37
  log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
@@ -63,7 +63,7 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
63
63
  shutil.copy(fw_file, destination)
64
64
  log.success("Done copying, resetting the board and wait for it to restart")
65
65
  if sys.platform in ["linux"]:
66
- dismount_uf2()
66
+ dismount_uf2_linux()
67
67
  for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True, refresh_per_second=2):
68
68
  time.sleep(1) # 5 secs to short on linux
69
69
  return mcu
@@ -88,7 +88,7 @@ def pumount(disk: UF2Disk):
88
88
  log.warning(f"{disk.label} already dismounted")
89
89
 
90
90
 
91
- def dismount_uf2():
91
+ def dismount_uf2_linux():
92
92
  global glb_dismount_me
93
93
  for disk in glb_dismount_me:
94
94
  pumount(disk)
@@ -3,11 +3,9 @@
3
3
  # sourcery skip: snake-case-functions
4
4
  from __future__ import annotations
5
5
 
6
- import subprocess
7
6
  import sys
8
7
  import time
9
8
  from pathlib import Path
10
- from typing import List
11
9
 
12
10
  from loguru import logger as log
13
11
  from rich.progress import track
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__
@@ -3,17 +3,14 @@ Translate board description to board designator
3
3
  """
4
4
 
5
5
  import functools
6
- import json
7
6
  from pathlib import Path
8
7
  from typing import Optional
9
8
 
10
9
  from mpflash.errors import MPFlashError
10
+ from mpflash.logger import log
11
+ from mpflash.mpboard_id.store import read_known_boardinfo
11
12
  from mpflash.vendor.versions import clean_version, get_stable_mp_version
12
13
 
13
- ###############################################################################################
14
- HERE = Path(__file__).parent
15
- ###############################################################################################
16
-
17
14
 
18
15
  def find_board_id_by_description(
19
16
  descr: str,
@@ -31,45 +28,43 @@ def find_board_id_by_description(
31
28
  board_info=board_info,
32
29
  version=clean_version(version) if version else None,
33
30
  )
34
- return boards[-1]["board"]
31
+ return boards[-1].board_id
35
32
  except MPFlashError:
36
33
  return "UNKNOWN_BOARD"
37
34
 
38
35
 
39
36
  @functools.lru_cache(maxsize=20)
40
37
  def _find_board_id_by_description(
41
- *, descr: str, short_descr: str, version: Optional[str] = None, board_info: Optional[Path] = None
38
+ *,
39
+ descr: str,
40
+ short_descr: str,
41
+ version: Optional[str] = None,
42
+ board_info: Optional[Path] = None,
42
43
  ):
43
44
  """
44
45
  Find the MicroPython BOARD_ID based on the description in the firmware
45
46
  using the pre-built board_info.json file
46
47
  """
47
- if not board_info:
48
- board_info = HERE / "board_info.json"
49
- if not board_info.exists():
50
- raise FileNotFoundError(f"Board info file not found: {board_info}")
51
48
 
52
- candidate_boards = _read_board_info(board_info)
49
+ candidate_boards = read_known_boardinfo(board_info)
53
50
 
54
51
  if version:
55
52
  # filter for matching version
56
53
  if version in ("preview", "stable"):
57
54
  # match last stable
58
55
  version = get_stable_mp_version()
59
- version_matches = [b for b in candidate_boards if b["version"].startswith(version)]
56
+ known_versions = sorted({b.version for b in candidate_boards})
57
+ if version not in known_versions:
58
+ # FIXME if latest stable is newer than the last version in the boardlist this will fail
59
+ log.trace(f"Version {version} not found in board info, using latest known version {known_versions[-1]}")
60
+ version = known_versions[-1]
61
+ version_matches = [b for b in candidate_boards if b.version.startswith(version)]
60
62
  if not version_matches:
61
63
  raise MPFlashError(f"No board info found for version {version}")
62
64
  candidate_boards = version_matches
63
- matches = [b for b in candidate_boards if b["description"] == descr]
65
+ matches = [b for b in candidate_boards if b.description == descr]
64
66
  if not matches and short_descr:
65
- matches = [b for b in candidate_boards if b["description"] == short_descr]
67
+ matches = [b for b in candidate_boards if b.description == short_descr]
66
68
  if not matches:
67
69
  raise MPFlashError(f"No board info found for description '{descr}' or '{short_descr}'")
68
- return sorted(matches, key=lambda x: x["version"])
69
-
70
-
71
- @functools.lru_cache(maxsize=20)
72
- def _read_board_info(board_info):
73
- with open(board_info, "r") as file:
74
- info = json.load(file)
75
- return info
70
+ return sorted(matches, key=lambda x: x.version)
Binary file
@@ -0,0 +1,42 @@
1
+ import functools
2
+ import zipfile
3
+ from pathlib import Path
4
+ from typing import List, Optional
5
+
6
+ import jsons
7
+
8
+ from mpflash.mpboard_id.board import Board
9
+
10
+ ###############################################################################################
11
+ HERE = Path(__file__).parent
12
+ ###############################################################################################
13
+
14
+
15
+ def write_boardinfo_json(board_list: List[Board], *, folder: Path):
16
+ """Writes the board information to JSON and CSV files.
17
+
18
+ Args:
19
+ board_list (List[Board]): The list of Board objects.
20
+ """
21
+ import zipfile
22
+
23
+ # create a zip file with the json file
24
+ with zipfile.ZipFile(folder / "board_info.zip", "w", compression=zipfile.ZIP_DEFLATED) as zipf:
25
+ # write the list to json file inside the zip
26
+ with zipf.open("board_info.json", "w") as fp:
27
+ fp.write(jsons.dumps(board_list, jdkwargs={"indent": 4}).encode())
28
+
29
+
30
+ @functools.lru_cache(maxsize=20)
31
+ def read_known_boardinfo(board_info: Optional[Path] = None) -> List[Board]:
32
+
33
+ if not board_info:
34
+ board_info = HERE / "board_info.zip"
35
+ if not board_info.exists():
36
+ raise FileNotFoundError(f"Board info file not found: {board_info}")
37
+
38
+ with zipfile.ZipFile(board_info, "r") as zf:
39
+ with zf.open("board_info.json", "r") as file:
40
+ info = jsons.loads(file.read().decode(encoding="utf-8"), List[Board])
41
+
42
+ return info