mpflash 1.24.5__py3-none-any.whl → 1.24.7__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/basicgit.py CHANGED
@@ -136,6 +136,9 @@ def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) ->
136
136
  return sorted(tags)
137
137
 
138
138
 
139
+ from github.GithubException import BadCredentialsException
140
+
141
+
139
142
  @cachetools.func.ttl_cache(maxsize=16, ttl=60) # 60 seconds
140
143
  def get_tags(repo: str, minver: Optional[str] = None) -> List[str]:
141
144
  """
@@ -176,7 +179,7 @@ def checkout_tag(tag: str, repo: Optional[Union[str, Path]] = None) -> bool:
176
179
  return True
177
180
 
178
181
 
179
- def sync_submodules(repo: Optional[Union[Path, str]] = None) -> bool:
182
+ def sync_submodules(repo: Union[Path, str]) -> bool:
180
183
  """
181
184
  make sure any submodules are in sync
182
185
  """
@@ -191,9 +194,26 @@ def sync_submodules(repo: Optional[Union[Path, str]] = None) -> bool:
191
194
  log.debug(result.stderr)
192
195
  else:
193
196
  return False
197
+ checkout_arduino_lib(Path(repo))
194
198
  return True
195
199
 
196
200
 
201
+ def checkout_arduino_lib(mpy_path: Path):
202
+ """
203
+ Checkout the arduino-lib submodule repo if it exists
204
+
205
+ This is needed as some of the arduino boards freeze modules originationg from the arduino-lib
206
+ """
207
+ # arduino_lib_path = mpy_path / "lib/arduino-lib"
208
+ if (mpy_path / "lib/arduino-lib").exists():
209
+ cmd = ["git", "submodule", "update", "--init", "lib/arduino-lib"]
210
+ try:
211
+ result = subprocess.run(cmd, cwd=mpy_path, check=True)
212
+ log.info(f"checkout arduino-lib: {result.returncode}")
213
+ except subprocess.CalledProcessError as e:
214
+ log.warning("Could not check out arduino-lib, error: ", e)
215
+
216
+
197
217
  def checkout_commit(commit_hash: str, repo: Optional[Union[Path, str]] = None) -> bool:
198
218
  """
199
219
  Checkout a specific commit
@@ -242,7 +262,7 @@ def switch_branch(branch: str, repo: Optional[Union[Path, str]] = None) -> bool:
242
262
 
243
263
  def fetch(repo: Union[Path, str]) -> bool:
244
264
  """
245
- fetches a repo
265
+ fetches a repo and all tags
246
266
  repo should be in the form of : path/.git
247
267
  repo = '../micropython/.git'
248
268
  returns True on success
@@ -12,7 +12,7 @@ from .touch1200 import enter_bootloader_touch_1200bps
12
12
 
13
13
  BL_OPTIONS = {
14
14
  "stm32": [BootloaderMethod.TOUCH_1200, BootloaderMethod.MPY, BootloaderMethod.MANUAL],
15
- "rp2": [BootloaderMethod.TOUCH_1200, BootloaderMethod.MPY, BootloaderMethod.MANUAL],
15
+ "rp2": [BootloaderMethod.MPY, BootloaderMethod.TOUCH_1200, BootloaderMethod.MANUAL],
16
16
  "samd": [BootloaderMethod.TOUCH_1200, BootloaderMethod.MPY, BootloaderMethod.MANUAL],
17
17
  "esp32": [BootloaderMethod.NONE],
18
18
  "esp8266": [BootloaderMethod.NONE],
mpflash/common.py CHANGED
@@ -7,10 +7,11 @@ from enum import Enum
7
7
  from pathlib import Path
8
8
  from typing import List, Optional, Union
9
9
 
10
- from github import Auth, Github
11
10
  from serial.tools import list_ports
12
11
  from serial.tools.list_ports_common import ListPortInfo
13
12
 
13
+ from mpflash.basicgit import GH_CLIENT as GH_CLIENT
14
+
14
15
  from .logger import log
15
16
 
16
17
  # from mpflash.mpremoteboard import MPRemoteBoard
@@ -27,17 +28,6 @@ PORT_FWTYPES = {
27
28
  "renesas-ra": [".hex"],
28
29
  }
29
30
 
30
- # Token with no permissions to avoid throttling
31
- # https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#getting-a-higher-rate-limit
32
- PAT_NO_ACCESS = (
33
- "github_pat_"
34
- + "11AAHPVFQ0G4NTaQ73Bw5J"
35
- + "_fAp7K9sZ1qL8VFnI9g78eUlCdmOXHB3WzSdj2jtEYb4XF3N7PDJBl32qIxq"
36
- )
37
-
38
- PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
39
- GH_CLIENT = Github(auth=Auth.Token(PAT))
40
-
41
31
 
42
32
  @dataclass
43
33
  class FWInfo:
@@ -150,9 +140,10 @@ def filtered_comports(
150
140
  comports = [
151
141
  p for p in list_ports.comports() if not any(fnmatch.fnmatch(p.device, i) for i in ignore)
152
142
  ]
153
-
143
+
154
144
  if False:
155
145
  import jsons
146
+
156
147
  print(jsons.dumps(comports).replace('{"description":', '\n{"description":'))
157
148
 
158
149
  if platform.system() == "Linux":
mpflash/downloaded.py CHANGED
@@ -14,11 +14,15 @@ from .config import config
14
14
  def downloaded_firmwares(fw_folder: Path) -> List[FWInfo]:
15
15
  """Load a list of locally downloaded firmwares from the jsonl file"""
16
16
  firmwares: List[FWInfo] = []
17
+ log.debug(f"Reading {fw_folder / 'firmware.jsonl' }")
17
18
  try:
18
19
  with jsonlines.open(fw_folder / "firmware.jsonl") as reader:
19
20
  firmwares = [FWInfo.from_dict(item) for item in reader]
20
21
  except FileNotFoundError:
21
22
  log.error(f"No firmware.jsonl found in {fw_folder}")
23
+ except jsonlines.InvalidLineError as e:
24
+ log.error(f"Invalid firmware.jsonl found in {fw_folder} : {e}")
25
+
22
26
  # sort by filename
23
27
  firmwares.sort(key=lambda x: x.filename)
24
28
  return firmwares
@@ -109,7 +113,11 @@ def filter_downloaded_fwlist(
109
113
  log.trace(f"Filtering firmware for {version} : {len(fw_list)} found.")
110
114
  # filter by port
111
115
  if port:
112
- fw_list = [fw for fw in fw_list if fw.port == port and Path(fw.firmware).suffix in PORT_FWTYPES[port]]
116
+ fw_list = [
117
+ fw
118
+ for fw in fw_list
119
+ if fw.port == port and Path(fw.firmware).suffix in PORT_FWTYPES[port]
120
+ ]
113
121
  log.trace(f"Filtering firmware for {port} : {len(fw_list)} found.")
114
122
 
115
123
  if board_id:
@@ -120,7 +128,7 @@ def filter_downloaded_fwlist(
120
128
  # the firmware variant should match exactly the board_id
121
129
  fw_list = [fw for fw in fw_list if fw.variant == board_id]
122
130
  log.trace(f"Filtering firmware for {board_id} : {len(fw_list)} found.")
123
-
131
+
124
132
  if selector and port in selector:
125
133
  fw_list = [fw for fw in fw_list if fw.filename.endswith(selector[port])]
126
134
  return fw_list
mpflash/flash/esp.py CHANGED
@@ -15,7 +15,7 @@ from mpflash.mpremoteboard import MPRemoteBoard
15
15
 
16
16
 
17
17
  def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optional[MPRemoteBoard]:
18
- if mcu.port not in ["esp32", "esp8266"] or mcu.board in ["ARDUINO_NANO_ESP32"]:
18
+ if mcu.port not in ["esp32", "esp8266"] or mcu.board.startswith("ARDUINO_"):
19
19
  log.error(f"esptool not supported for {mcu.port} {mcu.board} on {mcu.serialport}")
20
20
  return None
21
21
 
@@ -5,6 +5,7 @@ Flash SAMD and RP2 via UF2
5
5
  import shutil
6
6
  import sys
7
7
  from pathlib import Path
8
+ import time
8
9
  from typing import Optional
9
10
 
10
11
  import tenacity
@@ -38,7 +39,21 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
38
39
  log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
39
40
  return None
40
41
  if erase:
41
- log.warning("Erase not (yet) supported on .UF2, skipping erase.")
42
+ if mcu.port == "rp2":
43
+ rp2_erase =Path(__file__).parent.joinpath("../../vendor/pico-universal-flash-nuke/universal_flash_nuke.uf2").resolve()
44
+ log.info(f"Erasing {mcu.port} with {rp2_erase.name}")
45
+ # optimistic
46
+ destination = waitfor_uf2(board_id=mcu.port.upper())
47
+ if not destination :
48
+ log.error("Board is not in bootloader mode")
49
+ return None
50
+ copy_firmware_to_uf2(rp2_erase, destination)
51
+ if sys.platform in ["linux"]:
52
+ dismount_uf2_linux()
53
+ # allow for MCU restart after erase
54
+ time.sleep(0.5)
55
+ else:
56
+ log.warning(f"Erase not (yet) supported on .UF2, for port {mcu.port}, skipping erase.")
42
57
 
43
58
  destination = waitfor_uf2(board_id=mcu.port.upper())
44
59
 
@@ -84,5 +99,6 @@ def copy_firmware_to_uf2(fw_file: Path, destination: Path):
84
99
  Copy the firmware file to the destination,
85
100
  Retry 3 times with 1s delay
86
101
  """
87
- log.info(f"Copying {fw_file} to {destination}.")
102
+ log.trace(f"Firmware: {fw_file}")
103
+ log.info(f"Copying {fw_file.name} to {destination}.")
88
104
  return shutil.copy(fw_file, destination)
@@ -16,16 +16,18 @@ import mpflash.basicgit as git
16
16
  from mpflash.logger import log
17
17
  from mpflash.mpboard_id import Board
18
18
  from mpflash.mpboard_id.store import write_boardinfo_json
19
- from mpflash.versions import micropython_versions
19
+ from mpflash.versions import (get_preview_mp_version, get_stable_mp_version,
20
+ micropython_versions)
20
21
 
21
22
  # look for all mpconfigboard.h files and extract the board name
22
23
  # from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6"
23
24
  # and the #define MICROPY_HW_MCU_NAME "STM32F767xx"
24
25
  RE_H_MICROPY_HW_BOARD_NAME = re.compile(r"#define\s+MICROPY_HW_BOARD_NAME\s+\"(.+)\"")
25
26
  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]*)\"")
27
+ # find boards and variants in the mpconfigboard*.cmake files
28
+ RE_CMAKE_MICROPY_HW_BOARD_NAME = re.compile(
29
+ r"MICROPY_HW_BOARD_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\""
30
+ )
29
31
  RE_CMAKE_MICROPY_HW_MCU_NAME = re.compile(r"MICROPY_HW_MCU_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
30
32
  # TODO: normal make files
31
33
 
@@ -42,20 +44,21 @@ def boards_from_repo(mpy_path: Path, version: str, family: Optional[str] = None)
42
44
  """
43
45
  if not mpy_path.exists() or not mpy_path.is_dir():
44
46
  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
47
+ family = family or "micropython"
48
+ version = version or git.get_local_tag() # type: ignore
49
49
  if not version:
50
50
  raise ValueError("No version provided and no local tag found.")
51
+ elif version in ["main", "master"]:
52
+ version = get_preview_mp_version()
51
53
 
52
54
  board_list: List[Board] = []
53
55
  # look in mpconfigboard.h files
54
56
  board_list = boards_from_cmake(mpy_path, version, family)
55
57
 
56
- # look for variants in the .cmake files
58
+ # look for boards in the .cmake files
57
59
  board_list.extend(boards_from_headers(mpy_path, version, family))
58
- # TODO:? look for variants in the Makefile files
60
+
61
+ # TODO:? look for variants in the board.json files
59
62
 
60
63
  return board_list
61
64
 
@@ -63,7 +66,7 @@ def boards_from_repo(mpy_path: Path, version: str, family: Optional[str] = None)
63
66
  def boards_from_cmake(mpy_path: Path, version: str, family: str):
64
67
  """Get boards from the mpconfigboard.cmake files to the board_list."""
65
68
  board_list = []
66
- for path in mpy_path.glob("ports/**/mpconfigboard.cmake"):
69
+ for path in mpy_path.glob("ports/**/mpconfigboard*.cmake"):
67
70
  board = path.parent.name
68
71
  port = path.parent.parent.parent.name
69
72
  with open(path, "r") as f:
@@ -118,7 +121,9 @@ def boards_from_headers(mpy_path: Path, version: str, family: str):
118
121
  mcu_name = match[1]
119
122
  found += 1
120
123
  if found == 2:
121
- description = f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name
124
+ description = (
125
+ f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name
126
+ )
122
127
  board_list.append(
123
128
  Board(
124
129
  board_id=board,
@@ -160,6 +165,8 @@ def boards_for_versions(versions: List[str], mpy_path: Path):
160
165
  List[Board]: The list of Board objects.
161
166
  """
162
167
  board_list: List[Board] = []
168
+ # first fetch all tags from the repository
169
+ git.fetch(mpy_path)
163
170
  for version in track(versions, description="Searching MicroPython versions"):
164
171
  if git.checkout_tag(tag=version, repo=mpy_path):
165
172
  new_ones = boards_from_repo(mpy_path, version, family="micropython")
@@ -197,9 +204,10 @@ def make_table(board_list: List[Board]) -> rich.table.Table:
197
204
  is_wide = True
198
205
 
199
206
  table = rich.table.Table(title="MicroPython Board Information")
207
+ table.add_column("Port", justify="left", style="magenta")
200
208
  table.add_column("BOARD_ID", justify="left", style="green")
209
+ table.add_column("Variant(s)", justify="left", style="blue")
201
210
  table.add_column("Description", justify="left", style="cyan")
202
- table.add_column("Port", justify="left", style="magenta")
203
211
  table.add_column("Board Name", justify="left", style="blue")
204
212
  if is_wide:
205
213
  table.add_column("MCU Name", justify="left", style="blue")
@@ -209,7 +217,7 @@ def make_table(board_list: List[Board]) -> rich.table.Table:
209
217
  table.add_column("Family", justify="left", style="blue")
210
218
 
211
219
  for board in board_list:
212
- row = [board.board_id, board.description, *(board.port, board.board_name)]
220
+ row = [board.port, board.board_id, board.variant, board.description, board.board_name]
213
221
  if is_wide:
214
222
  row.append(board.mcu_name)
215
223
  row.extend((str(Path(board.path).suffix), board.version))
@@ -222,7 +230,13 @@ def make_table(board_list: List[Board]) -> rich.table.Table:
222
230
 
223
231
  def ask_mpy_path():
224
232
  """Ask the user for the path to the MicroPython repository."""
225
- questions = [inquirer.Text("mpy_path", message="Enter the path to the MicroPython repository", default=".\\repos\\micropython")]
233
+ questions = [
234
+ inquirer.Text(
235
+ "mpy_path",
236
+ message="Enter the path to the MicroPython repository",
237
+ default="./repos/micropython",
238
+ )
239
+ ]
226
240
  if answers := inquirer.prompt(questions):
227
241
  return Path(answers["mpy_path"])
228
242
  else:
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass, field
2
2
  from pathlib import Path
3
- from typing import Union
3
+ from typing import Union
4
4
 
5
5
 
6
6
  # - source : get_boardnames.py
@@ -20,7 +20,7 @@ class Board:
20
20
  family: str = field(default="micropython")
21
21
  mcu_name: str = field(default="")
22
22
  cpu: str = field(default="")
23
- # TODO: add variant
23
+ variant: str = field(default="")
24
24
 
25
25
  def __post_init__(self):
26
26
  if not self.cpu:
@@ -9,7 +9,7 @@ from typing import Optional
9
9
  from mpflash.errors import MPFlashError
10
10
  from mpflash.logger import log
11
11
  from mpflash.mpboard_id.store import read_known_boardinfo
12
- from mpflash.versions import clean_version, get_stable_mp_version
12
+ from mpflash.versions import clean_version, get_preview_mp_version, get_stable_mp_version
13
13
 
14
14
 
15
15
  def find_board_id_by_description(
@@ -64,13 +64,15 @@ def _find_board_id_by_description(
64
64
  short_descr = descr.split(" with ")[0]
65
65
  if version:
66
66
  # filter for matching version
67
- if version in ("preview", "stable"):
68
- # match last stable
67
+ if version in ("stable"):
69
68
  version = get_stable_mp_version()
69
+ if version in ("preview", "master"):
70
+ version = get_preview_mp_version()
70
71
  known_versions = sorted({b.version for b in candidate_boards})
71
72
  if version not in known_versions:
72
- log.trace(f"Version {version} not found in board info, using latest known version {known_versions[-1]}")
73
- version = '.'.join(known_versions[-1].split('.')[:2]) # take only major.minor
73
+ log.trace(known_versions)
74
+ log.debug(f"Version {version} not found in board info, using latest stable version {get_stable_mp_version()}")
75
+ version = ".".join(get_stable_mp_version().split(".")[:2]) # take only major.minor
74
76
  if version_matches := [b for b in candidate_boards if b.version.startswith(version)]:
75
77
  candidate_boards = version_matches
76
78
  else:
Binary file
@@ -55,7 +55,7 @@ class MPRemoteBoard:
55
55
  self.arch = ""
56
56
  self.mpy = ""
57
57
  self.build = ""
58
- self.location = location # USB location
58
+ self.location = location # USB location
59
59
  self.toml = {}
60
60
  if update:
61
61
  self.get_mcu_info()
@@ -97,7 +97,9 @@ class MPRemoteBoard:
97
97
 
98
98
  if sys.platform == "win32":
99
99
  # Windows sort of comports by number - but fallback to device name
100
- return sorted(output, key=lambda x: int(x.split()[0][3:]) if x.split()[0][3:].isdigit() else x)
100
+ return sorted(
101
+ output, key=lambda x: int(x.split()[0][3:]) if x.split()[0][3:].isdigit() else x
102
+ )
101
103
  # sort by device name
102
104
  return sorted(output)
103
105
 
@@ -116,9 +118,10 @@ class MPRemoteBoard:
116
118
  ["run", str(HERE / "mpy_fw_info.py")],
117
119
  no_info=True,
118
120
  timeout=timeout,
119
- resume=True, # Avoid restarts
121
+ resume=False, # Avoid restarts
120
122
  )
121
- if rc != OK:
123
+ if rc:
124
+ log.debug(f"rc: {rc}, result: {result}")
122
125
  raise ConnectionError(f"Failed to get mcu_info for {self.serialport}")
123
126
  # Ok we have the info, now parse it
124
127
  raw_info = result[0].strip()
@@ -134,7 +137,9 @@ class MPRemoteBoard:
134
137
  self.description = descr = info["board"]
135
138
  pos = descr.rfind(" with")
136
139
  short_descr = descr[:pos].strip() if pos != -1 else ""
137
- if board_name := find_board_id_by_description(descr, short_descr, version=self.version):
140
+ if board_name := find_board_id_by_description(
141
+ descr, short_descr, version=self.version
142
+ ):
138
143
  self.board = board_name
139
144
  else:
140
145
  self.board = "UNKNOWN_BOARD"
@@ -174,7 +179,7 @@ class MPRemoteBoard:
174
179
  except Exception as e:
175
180
  log.error(f"Failed to parse board_info.toml: {e}")
176
181
  else:
177
- log.trace(f"Failed to read board_info.toml: {result}")
182
+ log.trace(f"Did not find a board_info.toml: {result}")
178
183
 
179
184
  def disconnect(self) -> bool:
180
185
  """
@@ -202,7 +207,7 @@ class MPRemoteBoard:
202
207
  log_errors: bool = True,
203
208
  no_info: bool = False,
204
209
  timeout: int = 60,
205
- resume: bool = False,
210
+ resume: Optional[bool] = None,
206
211
  **kwargs,
207
212
  ):
208
213
  """
@@ -223,7 +228,7 @@ class MPRemoteBoard:
223
228
  if self.serialport:
224
229
  prefix += ["connect", self.serialport]
225
230
  # if connected add resume to keep state between commands
226
- if self.connected or resume:
231
+ if (resume != False) and self.connected or resume:
227
232
  prefix += ["resume"]
228
233
  cmd = prefix + cmd
229
234
  log.debug(" ".join(cmd))
@@ -0,0 +1,185 @@
1
+ """
2
+ The micropython git repo contains many 'board.json' files.
3
+
4
+ This is an example:
5
+ ports/stm32/boards/PYBV11/board.json
6
+
7
+ {
8
+ "deploy": [
9
+ "../PYBV10/deploy.md"
10
+ ],
11
+ "docs": "",
12
+ "features": [],
13
+ "images": [
14
+ "PYBv1_1.jpg",
15
+ "PYBv1_1-C.jpg",
16
+ "PYBv1_1-E.jpg"
17
+ ],
18
+ "mcu": "stm32f4",
19
+ "product": "Pyboard v1.1",
20
+ "thumbnail": "",
21
+ "url": "https://store.micropython.org/product/PYBv1.1",
22
+ "variants": {
23
+ "DP": "Double-precision float",
24
+ "DP_THREAD": "Double precision float + Threads",
25
+ "NETWORK": "Wiznet 5200 Driver",
26
+ "THREAD": "Threading"
27
+ },
28
+ "vendor": "George Robotics"
29
+ }
30
+
31
+ This module implements `class Database` which reads all 'board.json' files and
32
+ provides a way to browse it's data.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import json
38
+ from dataclasses import dataclass, field
39
+ from glob import glob
40
+ from pathlib import Path
41
+
42
+
43
+ @dataclass(order=True)
44
+ class Variant:
45
+ name: str
46
+ """
47
+ Example: "DP_THREAD"
48
+ """
49
+ text: str
50
+ """
51
+ Example: "Double precision float + Threads"
52
+ """
53
+ board: Board = field(repr=False)
54
+
55
+
56
+ @dataclass(order=True)
57
+ class Board:
58
+ name: str
59
+ """
60
+ Example: "PYBV11"
61
+ """
62
+ variants: list[Variant]
63
+ """
64
+ List of variants available for this board.
65
+ Variants are sorted. May be an empty list if no variants are available.
66
+ Example key: "DP_THREAD"
67
+ """
68
+ url: str
69
+ """
70
+ Primary URL to link to this board.
71
+ """
72
+ mcu: str
73
+ """
74
+ Example: "stm32f4"
75
+ """
76
+ product: str
77
+ """
78
+ Example: "Pyboard v1.1"
79
+ """
80
+ vendor: str
81
+ """
82
+ Example: "George Robotics"
83
+ """
84
+ images: list[str]
85
+ """
86
+ Images of this board, stored in the micropython-media repository.
87
+ Example: ["PYBv1_1.jpg", "PYBv1_1-C.jpg", "PYBv1_1-E.jpg"]
88
+ """
89
+ deploy: list[str]
90
+ """
91
+ Files that explain how to deploy for this board:
92
+ Example: ["../PYBV10/deploy.md"]
93
+ """
94
+ port: Port | None = field(default=None, compare=False)
95
+
96
+ @staticmethod
97
+ def factory(filename_json: Path) -> Board:
98
+ with filename_json.open() as f:
99
+ board_json = json.load(f)
100
+
101
+ board = Board(
102
+ name=filename_json.parent.name,
103
+ variants=[],
104
+ url=board_json["url"],
105
+ mcu=board_json["mcu"],
106
+ product=board_json["product"],
107
+ vendor=board_json["vendor"],
108
+ images=board_json["images"],
109
+ deploy=board_json["deploy"],
110
+ )
111
+ board.variants.extend(
112
+ sorted([Variant(*v, board) for v in board_json.get("variants", {}).items()]) # type: ignore
113
+ )
114
+ return board
115
+
116
+
117
+ @dataclass(order=True)
118
+ class Port:
119
+ name: str
120
+ """
121
+ Example: "stm32"
122
+ """
123
+ boards: dict[str, Board] = field(default_factory=dict, repr=False)
124
+ """
125
+ Example key: "PYBV11"
126
+ """
127
+
128
+
129
+ @dataclass
130
+ class Database:
131
+ """
132
+ This database contains all information retrieved from all 'board.json' files.
133
+ """
134
+
135
+ mpy_root_directory: Path = field(repr=False)
136
+ port_filter: str = field(default="", repr=False)
137
+
138
+ ports: dict[str, Port] = field(default_factory=dict)
139
+ boards: dict[str, Board] = field(default_factory=dict)
140
+
141
+ def __post_init__(self) -> None:
142
+ mpy_dir = self.mpy_root_directory
143
+ # Take care to avoid using Path.glob! Performance was 15x slower.
144
+ for p in glob(f"{mpy_dir}/ports/**/boards/**/board.json"):
145
+ filename_json = Path(p)
146
+ port_name = filename_json.parent.parent.parent.name
147
+ if self.port_filter and self.port_filter != port_name:
148
+ continue
149
+
150
+ # Create a port
151
+ port = self.ports.get(port_name, None)
152
+ if port is None:
153
+ port = Port(port_name)
154
+ self.ports[port_name] = port
155
+
156
+ # Load board.json and attach it to the board
157
+ board = Board.factory(filename_json)
158
+ board.port = port
159
+
160
+ port.boards[board.name] = board
161
+ self.boards[board.name] = board
162
+
163
+ # Add 'special' ports, that don't have boards
164
+ # TODO(mst) Tidy up later (variant descriptions etc)
165
+ for special_port_name in ["unix", "webassembly", "windows"]:
166
+ if self.port_filter and self.port_filter != special_port_name:
167
+ continue
168
+ path = Path(mpy_dir, "ports", special_port_name)
169
+ variant_names = [var.name for var in path.glob("variants/*") if var.is_dir()]
170
+ board = Board(
171
+ special_port_name,
172
+ [],
173
+ f"https://github.com/micropython/micropython/blob/master/ports/{special_port_name}/README.md",
174
+ "",
175
+ "",
176
+ "",
177
+ [],
178
+ [],
179
+ )
180
+ board.variants = [Variant(v, "", board) for v in variant_names]
181
+ port = Port(special_port_name, {special_port_name: board})
182
+ board.port = port
183
+
184
+ self.ports[special_port_name] = port
185
+ self.boards[board.name] = board
@@ -0,0 +1,21 @@
1
+ Copyright 2024 Phil Howard
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4
+ following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7
+ disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
10
+ disclaimer in the documentation and/or other materials provided with the distribution.
11
+
12
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
21
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mpflash/vendor/readme.md CHANGED
@@ -1,3 +1,14 @@
1
1
  These modules are vendored from the following repositories:
2
2
 
3
- micropython/micropython
3
+ - https://github.com/micropython/micropython (MIT)
4
+ - dfu.py
5
+ - pydfy.py
6
+
7
+ - https://github.com/mattytrentini/mpbuild (MIT)
8
+ - board_database.py
9
+
10
+ - https://github.com/click-contrib/click-aliases (Public Domain)
11
+ - click_aliases.py (Robbin Bonthond)
12
+
13
+ - https://github.com/Gadgetoid/pico-universal-flash-nuke
14
+ - universal_flash_nuke.uf2
mpflash/versions.py CHANGED
@@ -81,34 +81,16 @@ def micropython_versions(minver: str = "v1.20", reverse: bool = False, cache_it=
81
81
  try:
82
82
  gh_client = GH_CLIENT
83
83
  repo = gh_client.get_repo("micropython/micropython")
84
- versions = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
84
+ tags = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
85
+ versions = [v for v in tags if not v.endswith(V_PREVIEW)]
85
86
  # Only keep the last preview
86
- versions = [v for v in versions if not v.endswith(V_PREVIEW) or v == versions[-1]]
87
+ preview = sorted([v for v in tags if v.endswith(V_PREVIEW)], reverse=True)[0]
88
+ versions.append(preview)
87
89
  except Exception as e:
88
- versions = [
89
- "v9.99.9-preview",
90
- "v1.22.2",
91
- "v1.22.1",
92
- "v1.22.0",
93
- "v1.21.1",
94
- "v1.21.0",
95
- "v1.20.0",
96
- "v1.19.1",
97
- "v1.19",
98
- "v1.18",
99
- "v1.17",
100
- "v1.16",
101
- "v1.15",
102
- "v1.14",
103
- "v1.13",
104
- "v1.12",
105
- "v1.11",
106
- "v1.10",
107
- ]
108
- cache_it = False
109
- versions = [v for v in versions if parse(v) >= parse(minver)]
110
- # remove all but the most recent (preview) version
111
- versions = versions[:1] + [v for v in versions if "preview" not in v]
90
+ log.error(e)
91
+ versions = []
92
+ # returns - but does not cache
93
+ raise NoCacheCondition(function_value=versions)
112
94
  # remove any duplicates and sort
113
95
  versions = sorted(list(set(versions)), reverse=reverse, key=lambda s: (not is_version(s), s))
114
96
  if cache_it:
@@ -117,16 +99,18 @@ def micropython_versions(minver: str = "v1.20", reverse: bool = False, cache_it=
117
99
  raise NoCacheCondition(function_value=versions)
118
100
 
119
101
 
120
- def get_stable_mp_version() -> str:
102
+ def get_stable_mp_version(cache_it=True) -> str:
121
103
  # read the versions from the git tags
122
- all_versions = micropython_versions(minver=OLDEST_VERSION)
123
- return [v for v in all_versions if not v.endswith(V_PREVIEW)][-1]
104
+ all_versions = micropython_versions(minver=OLDEST_VERSION, cache_it=cache_it)
105
+ versions = [v for v in all_versions if not v.endswith(V_PREVIEW)]
106
+ return versions[-1] if versions else ""
124
107
 
125
108
 
126
- def get_preview_mp_version() -> str:
109
+ def get_preview_mp_version(cache_it=True) -> str:
127
110
  # read the versions from the git tags
128
- all_versions = micropython_versions(minver=OLDEST_VERSION)
129
- return [v for v in all_versions if v.endswith(V_PREVIEW)][-1]
111
+ all_versions = micropython_versions(minver=OLDEST_VERSION, cache_it=cache_it)
112
+ versions = [v for v in all_versions if v.endswith(V_PREVIEW)]
113
+ return versions[0] if versions else ""
130
114
 
131
115
 
132
116
  # Do not cache , same path will have different versions checked out
@@ -1,19 +1,19 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: mpflash
3
- Version: 1.24.5
3
+ Version: 1.24.7
4
4
  Summary: Flash and download tool for MicroPython firmwares
5
- Home-page: https://github.com/Josverl/micropython-stubber/blob/main/src/mpflash/README.md
6
5
  License: MIT
7
6
  Keywords: MicroPython,firmware,flash,download,UF2,esptool
8
7
  Author: Jos Verlinde
9
8
  Author-email: jos_verlinde@hotmail.com
10
- Requires-Python: >=3.8.1,<4.0
9
+ Requires-Python: >=3.9,<4.0
11
10
  Classifier: License :: OSI Approved :: MIT License
12
11
  Classifier: Programming Language :: Python :: 3
13
12
  Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
17
  Classifier: Programming Language :: Python :: Implementation :: CPython
18
18
  Classifier: Programming Language :: Python :: Implementation :: MicroPython
19
19
  Classifier: Topic :: Software Development :: Build Tools
@@ -29,14 +29,16 @@ Requires-Dist: jsons (>=1.6.3,<2.0.0)
29
29
  Requires-Dist: libusb (>=1.0.27,<2.0.0) ; sys_platform == "win32"
30
30
  Requires-Dist: loguru (>=0.7.2,<0.8.0)
31
31
  Requires-Dist: mpremote (>=1.22.0,<2.0.0)
32
- Requires-Dist: packaging (==23.2)
32
+ Requires-Dist: packaging (>=24.2,<25.0)
33
33
  Requires-Dist: platformdirs (>=4.2.0,<5.0.0)
34
+ Requires-Dist: poetry (>=2.0.1,<3.0.0)
34
35
  Requires-Dist: psutil (>=5.9.8,<6.0.0)
35
36
  Requires-Dist: pygithub (>=2.1.1,<3.0.0)
36
37
  Requires-Dist: pyusb (>=1.2.1,<2.0.0)
37
38
  Requires-Dist: requests (>=2.31.0,<3.0.0)
38
39
  Requires-Dist: rich-click (>=1.8.1,<2.0.0)
39
40
  Requires-Dist: tenacity (==8.2.3)
41
+ Project-URL: Homepage, https://github.com/Josverl/micropython-stubber/blob/main/src/mpflash/README.md
40
42
  Project-URL: Repository, https://github.com/Josverl/micropython-stubber
41
43
  Description-Content-Type: text/markdown
42
44
 
@@ -1,9 +1,9 @@
1
1
  mpflash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  mpflash/add_firmware.py,sha256=1h0HsA-EVi3HXLmoEvzwY_a-GuWYzPwulTYHHBB8THg,3428
3
3
  mpflash/ask_input.py,sha256=RJHGGrhYniSu-bdoKnKptE3DtpiCREJGRTZmFazvG-E,8946
4
- mpflash/basicgit.py,sha256=6dKexwx844R55q6X39ZDCF4HDB3t94OHyDJuuicZvjw,9959
4
+ mpflash/basicgit.py,sha256=NO27JTPUsnMWQ2bKI_zsIFsFTfCZO3QKbvQ23kIehxU,10734
5
5
  mpflash/bootloader/__init__.py,sha256=Qy3E6tETPnzMga9LgD5UgOvJ0zZIBEqhtEVb4v8CTWQ,107
6
- mpflash/bootloader/activate.py,sha256=FlO4XQlKyoOuvmDdj_0u_mjNPhjGwB_K17jQ-8nSXRA,2361
6
+ mpflash/bootloader/activate.py,sha256=orQOw4XTkXVZI-rMInRb0T5Wp3qA_BlzbJUA2gyBToU,2361
7
7
  mpflash/bootloader/detect.py,sha256=fBrILi7-ICRaregqms3PYqwiQVAJC0rXVhpyzDkoPQI,2690
8
8
  mpflash/bootloader/manual.py,sha256=2IfxrTFRso78FezVMTOrXMEojamFDDTXrAM9g1SbjNA,3170
9
9
  mpflash/bootloader/micropython.py,sha256=v_kZkvg0uWZDbMrT78gmiYHbD83QLdnrctvEClI8iRg,529
@@ -13,17 +13,17 @@ mpflash/cli_flash.py,sha256=pVqEsDocDT3KmIMTpXdym-ZlzThLSIp6oVtYib65dys,7595
13
13
  mpflash/cli_group.py,sha256=VWwYHiPVV19sQEr5lL8LlcPyZ-A6Gs79eMDJy8LLt90,2615
14
14
  mpflash/cli_list.py,sha256=ja21AZ4yghGTtOHkEtV1EOmT6EYxOiU2gzJc-mZaDto,2427
15
15
  mpflash/cli_main.py,sha256=5EkvzsqOUDXvNaW814oSUcPWeNhnwh78Sg0MteDv_fk,1133
16
- mpflash/common.py,sha256=uNd9dUbPQE4KT-p3Y-od8jVE51b3IcHxDWHpY5vi6Yo,7964
16
+ mpflash/common.py,sha256=umTouKs5Wb7Q2zOaaoO9nqmlpHIaiDBRIGYIkZMBVO8,7558
17
17
  mpflash/config.py,sha256=tdpvAvAlpco1GfeG2evn5tAKYluLEanqwrrvkir7QcQ,1073
18
18
  mpflash/connected.py,sha256=woYhuXoWpfzRMDUpBLVQZbVTGtMsKWNd5z1rsR1ELXA,3578
19
19
  mpflash/download.py,sha256=wE4uBSFFMAKOBH4jwHweL0wVYh4vi74t1673ku_IeoA,14305
20
- mpflash/downloaded.py,sha256=5qrf-V1fNTTGeueXaZUAnEqa-Yqru2jLyztx0wdyeuc,4938
20
+ mpflash/downloaded.py,sha256=nxTWU4lvhcthqvVmzpYHnXCnjD8wJv0KFq2KtaoTO1A,5160
21
21
  mpflash/errors.py,sha256=IAidY3qkZsXy6Pm1rdmVFmGyg81ywHhse3itaPctA2w,247
22
22
  mpflash/flash/__init__.py,sha256=g4Fp8MiquaDZXIvRJwYRkkll1MMyRud7x6qmwCk9Lgo,2096
23
- mpflash/flash/esp.py,sha256=_VfbyYIEaUgAjsD66tjbdgYL6ElkSAfpIMKz6q4QKig,2287
23
+ mpflash/flash/esp.py,sha256=CRF8sfIyuDZoYC1luBy92zLm6Qz3dnBBWjuJ3A4_crE,2284
24
24
  mpflash/flash/stm32.py,sha256=dqp9BZ4Vr-6GlQcF12TSmRf-5TXkov9qvCpMgeUJc7Y,574
25
25
  mpflash/flash/stm32_dfu.py,sha256=W-3JsRQyf3DduoIRXDmGZ35RogqtjQgcJnk-GOtQoLE,3090
26
- mpflash/flash/uf2/__init__.py,sha256=ucTg1pg7TruwPjecCPWYRpCwWPz-rngv7UYaQ_bMzww,2854
26
+ mpflash/flash/uf2/__init__.py,sha256=haL84hP2p1ZjKF6dXJJHAB_NTf7jT91MuZvmvg9SpIA,3617
27
27
  mpflash/flash/uf2/boardid.py,sha256=U5wGM8VA3wEpUxQCMtuXpMZZomdVH8J_Zd5_GekUMuU,423
28
28
  mpflash/flash/uf2/linux.py,sha256=UgoF_GhJdxA2NvfcFNV69YuDg3v4ueLM0epsDzmfKK0,4440
29
29
  mpflash/flash/uf2/macos.py,sha256=TGGijlnMJy3RDhdPS3WsPOE2lWFOQbqC3xn4cxLu8GA,1229
@@ -33,21 +33,24 @@ mpflash/flash/worklist.py,sha256=owS3xJbWC-SzbK9z6jQER0Kat3OIV09IxnV-f-tjGlY,599
33
33
  mpflash/list.py,sha256=lP_S5xbC0Men9HsXcIxOsP0bFRlCYh5CynMLFJx8cEE,3607
34
34
  mpflash/logger.py,sha256=dI_H_a7EOdQJyvoeRHQuYeZuTKYVUS3DUPTLhE9rkdM,1098
35
35
  mpflash/mpboard_id/__init__.py,sha256=b9PJiKFqmfyYgfi0-pbWGp2mrljdgvO6DNy0ABS8izU,3898
36
- mpflash/mpboard_id/add_boards.py,sha256=47TtN98FVc6PvuOr-3-g3LacYW8JvXpM5Gr_jhdUGEU,9630
37
- mpflash/mpboard_id/board.py,sha256=CwtBux8y7GDUe7CADVxL8YefGRl9Fg8OAJBUhgaBYCI,1151
38
- mpflash/mpboard_id/board_id.py,sha256=MnDWPqp0OqCkWD3E1Mhg-g20qASgPVHdROOCdr5TpOU,3249
39
- mpflash/mpboard_id/board_info.zip,sha256=XkIk35v6LotRMClCU-zEvo1zQiKXZAqkHfwLP4JhfaM,20102
36
+ mpflash/mpboard_id/add_boards.py,sha256=xBC9DULWq6nR1Nui2Z7tiLHxC0QohPXgJOYvSgrGwEM,10063
37
+ mpflash/mpboard_id/board.py,sha256=JKb4T67HmK7widW-4c1PgilvywMbZYToLk9Fyokm-6Q,1163
38
+ mpflash/mpboard_id/board_id.py,sha256=A2os0LU7-i_0JuEcMc1PbLt79TiAq16qRl7fv5uzsA8,3374
39
+ mpflash/mpboard_id/board_info.zip,sha256=0rQkVLOBRg5dwvkLmJtRdCXwKiA9arkWYVH7N8dYuNw,21720
40
40
  mpflash/mpboard_id/store.py,sha256=n85vnUAxGKv1C23wkm22ZFAFGK6AZZiCFvc1lGJJjis,1703
41
- mpflash/mpremoteboard/__init__.py,sha256=3F6vZHM1znUOnAo0ne-FalApM6vwbTNYg4kJwkS1gNI,9521
41
+ mpflash/mpremoteboard/__init__.py,sha256=Vydc7jZai32lrGTUjwylZT9U8yulsgLIk39mnuI_k9I,9666
42
42
  mpflash/mpremoteboard/mpy_fw_info.py,sha256=eRjhqN7MpmYE9TiS4iukquZZs3QE_lD5zv_vOPSjNrk,4821
43
43
  mpflash/mpremoteboard/runner.py,sha256=-PgzAeBGbyXaAUlwyiw4mcINsP2U1XRRjP1_QdBrxpg,4786
44
+ mpflash/vendor/board_database.py,sha256=QE3oXj96oTAsx94gNfHMYWu_RgBTHW1v9Wp5dq_Dt-Q,5253
44
45
  mpflash/vendor/click_aliases.py,sha256=c853EHSlkE2DvFqeFvFpwXKuJj3_jsXDP7iotVOKaAw,3156
45
46
  mpflash/vendor/dfu.py,sha256=ZXMcE6aH4-43Wh4tbQT4U-q-BU3RUiL3JAxmP_QAK2s,5755
47
+ mpflash/vendor/pico-universal-flash-nuke/LICENSE.txt,sha256=Zkc2iTNbib2NCMwtLjMEz0vFCPglgvaw6Mj7QiWldpQ,1484
48
+ mpflash/vendor/pico-universal-flash-nuke/universal_flash_nuke.uf2,sha256=QuPMppqHMVOt3vDVU0bikHRLsTiDRQYNUcGQ_OLRFGI,28160
46
49
  mpflash/vendor/pydfu.py,sha256=_MdBRo1EeNeKDqFPSTB5tNL1jGSBJgsVeVjE5e7Pb8s,20542
47
- mpflash/vendor/readme.md,sha256=iIIZxuLUIGHQ0KODzYVtMezsztvyxCXcNJp_AzwTIPk,86
48
- mpflash/versions.py,sha256=pKl-4GPHTs3_p73xywxSvsouZ3L4R2ljXSCkWj9U8HE,4742
49
- mpflash-1.24.5.dist-info/entry_points.txt,sha256=Jk_visOhYOsZIcSP2Ms9hKqfKy1iorR-6dYltSoWCpY,52
50
- mpflash-1.24.5.dist-info/LICENSE,sha256=mWpNhsIxWzetYNnTpr4eb3HtgsxGIC8KcYWxXEcxQvE,1077
51
- mpflash-1.24.5.dist-info/METADATA,sha256=mJfXKJ0R5EkBVnMLyRoixlpC2DmgbTj3ehYAdoTTzw4,17651
52
- mpflash-1.24.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
53
- mpflash-1.24.5.dist-info/RECORD,,
50
+ mpflash/vendor/readme.md,sha256=BQ7Uxf8joeYMjTUuSLLBG49ob6a9MgFPIEwuc72-Mfw,415
51
+ mpflash/versions.py,sha256=qGkE2LTzQ1QDyHc9-wzsHsRrN7PWK69xt0Vq3EVojms,4452
52
+ mpflash-1.24.7.dist-info/entry_points.txt,sha256=Jk_visOhYOsZIcSP2Ms9hKqfKy1iorR-6dYltSoWCpY,52
53
+ mpflash-1.24.7.dist-info/LICENSE,sha256=mWpNhsIxWzetYNnTpr4eb3HtgsxGIC8KcYWxXEcxQvE,1077
54
+ mpflash-1.24.7.dist-info/METADATA,sha256=kkeoaVrJt6i62yFsvXWdczncaJ3bz3055X05Ijt6z-g,17757
55
+ mpflash-1.24.7.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
56
+ mpflash-1.24.7.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.0.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any