mpflash 0.7.7__py3-none-any.whl → 0.8.1__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.
@@ -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
@@ -0,0 +1,288 @@
1
+ """
2
+ Simple Git module, where needed via powershell
3
+
4
+ Some of the functions are based on the PyGithub module
5
+ """
6
+
7
+ import os
8
+ import subprocess
9
+ from pathlib import Path
10
+ from typing import List, Optional, Union
11
+
12
+ import cachetools.func
13
+ from github import Auth, Github
14
+ from loguru import logger as log
15
+ from packaging.version import parse
16
+
17
+ # from stubber.utils.versions import SET_PREVIEW
18
+
19
+ # Token with no permissions to avoid throttling
20
+ # 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
21
+ PAT_NO_ACCESS = (
22
+ "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
23
+ )
24
+ PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
25
+ GH_CLIENT = Github(auth=Auth.Token(PAT))
26
+
27
+
28
+ def _run_local_git(
29
+ cmd: List[str],
30
+ repo: Optional[Union[Path, str]] = None,
31
+ expect_stderr=False,
32
+ capture_output=True,
33
+ echo_output=True,
34
+ ):
35
+ "run a external (git) command in the repo's folder and deal with some of the errors"
36
+ try:
37
+ if repo:
38
+ if isinstance(repo, str):
39
+ repo = Path(repo)
40
+ result = subprocess.run(
41
+ cmd, capture_output=capture_output, check=True, cwd=repo.absolute().as_posix(), encoding="utf-8"
42
+ )
43
+ else:
44
+ result = subprocess.run(cmd, capture_output=capture_output, check=True, encoding="utf-8")
45
+ except (NotADirectoryError, FileNotFoundError) as e: # pragma: no cover
46
+ return None
47
+ except subprocess.CalledProcessError as e: # pragma: no cover
48
+ # add some logging for github actions
49
+ log.error(f"{str(e)} : { e.stderr}")
50
+ return None
51
+ if result.stderr and result.stderr != b"":
52
+ stderr = result.stderr
53
+ if "cloning into" in stderr.lower():
54
+ # log.info(stderr)
55
+ expect_stderr = True
56
+ if "warning" in stderr.lower():
57
+ log.warning(stderr)
58
+ expect_stderr = True
59
+ elif capture_output and echo_output: # pragma: no cover
60
+ log.info(stderr)
61
+ if not expect_stderr:
62
+ raise ChildProcessError(stderr)
63
+
64
+ if result.returncode and result.returncode < 0:
65
+ raise ChildProcessError(result.stderr)
66
+ return result
67
+
68
+
69
+ def clone(remote_repo: str, path: Path, shallow: bool = False, tag: Optional[str] = None) -> bool:
70
+ """git clone [--depth 1] [--branch <tag_name>] <remote> <directory>"""
71
+ cmd = ["git", "clone"]
72
+ if shallow:
73
+ cmd += ["--depth", "1"]
74
+ if tag in {"preview", "latest", "master"}:
75
+ tag = None
76
+ cmd += [remote_repo, "--branch", tag, str(path)] if tag else [remote_repo, str(path)]
77
+ if result := _run_local_git(cmd, expect_stderr=True, capture_output=False):
78
+ return result.returncode == 0
79
+ else:
80
+ return False
81
+
82
+
83
+ def get_local_tag(repo: Optional[Union[str, Path]] = None, abbreviate: bool = True) -> Union[str, None]:
84
+ """
85
+ get the most recent git version tag of a local repo
86
+ repo Path should be in the form of : repo = "./repo/micropython"
87
+
88
+ returns the tag or None
89
+ """
90
+ if not repo:
91
+ repo = Path(".")
92
+ elif isinstance(repo, str):
93
+ repo = Path(repo)
94
+
95
+ result = _run_local_git(
96
+ # ["git", "describe", "--tags"],
97
+ ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
98
+ repo=repo.as_posix(),
99
+ expect_stderr=True,
100
+ )
101
+ if not result:
102
+ return None
103
+ tag: str = result.stdout
104
+ tag = tag.replace("\r", "").replace("\n", "")
105
+ if not abbreviate or "-" not in tag:
106
+ return tag
107
+ if "-preview" in tag:
108
+ tag = tag.split("-preview")[0] + "-preview"
109
+ return tag
110
+
111
+
112
+ def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) -> List[str]:
113
+ """
114
+ get list of all tags of a local repo
115
+ """
116
+ if not repo:
117
+ repo = Path(".")
118
+
119
+ result = _run_local_git(["git", "tag", "-l"], repo=repo.as_posix(), expect_stderr=True)
120
+ if not result or result.returncode != 0:
121
+ return []
122
+ tags = result.stdout.replace("\r", "").split("\n")
123
+ tags = [tag for tag in tags if tag.startswith("v")]
124
+ if minver:
125
+ tags = [tag for tag in tags if parse(tag) >= parse(minver)]
126
+ return sorted(tags)
127
+
128
+
129
+ @cachetools.func.ttl_cache(maxsize=16, ttl=60) # 60 seconds
130
+ def get_tags(repo: str, minver: Optional[str] = None) -> List[str]:
131
+ """
132
+ Get list of tag of a repote github repo
133
+ """
134
+ if not repo or not isinstance(repo, str) or "/" not in repo: # type: ignore
135
+ return []
136
+ try:
137
+ gh_repo = GH_CLIENT.get_repo(repo)
138
+ except ConnectionError as e:
139
+ # TODO: unable to capture the exeption
140
+ log.warning(f"Unable to get tags - {e}")
141
+ return []
142
+ tags = [tag.name for tag in gh_repo.get_tags()]
143
+ if minver:
144
+ tags = [tag for tag in tags if parse(tag) >= parse(minver)]
145
+ return sorted(tags)
146
+
147
+
148
+ def checkout_tag(tag: str, repo: Optional[Union[str, Path]] = None) -> bool:
149
+ """
150
+ checkout a specific git tag
151
+ """
152
+ cmd = ["git", "checkout", tag, "--quiet", "--force"]
153
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True, capture_output=True)
154
+ if not result:
155
+ return False
156
+ # actually a good result
157
+ msg = {result.stdout}
158
+ if msg != {""}:
159
+ log.warning(f"git message: {msg}")
160
+ return True
161
+
162
+
163
+ def sync_submodules(repo: Optional[Union[Path, str]] = None) -> bool:
164
+ """
165
+ make sure any submodules are in sync
166
+ """
167
+ cmds = [
168
+ ["git", "submodule", "sync", "--quiet"],
169
+ # ["git", "submodule", "update", "--quiet"],
170
+ ["git", "submodule", "update", "--init", "lib/micropython-lib"],
171
+ ]
172
+ for cmd in cmds:
173
+ if result := _run_local_git(cmd, repo=repo, expect_stderr=True):
174
+ # actually a good result
175
+ log.debug(result.stderr)
176
+ else:
177
+ return False
178
+ return True
179
+
180
+
181
+ def checkout_commit(commit_hash: str, repo: Optional[Union[Path, str]] = None) -> bool:
182
+ """
183
+ Checkout a specific commit
184
+ """
185
+ cmd = ["git", "checkout", commit_hash, "--quiet", "--force"]
186
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
187
+ if not result:
188
+ return False
189
+ # actually a good result
190
+ log.debug(result.stderr)
191
+ return True
192
+
193
+
194
+ def switch_tag(tag: Union[str, Path], repo: Optional[Union[Path, str]] = None) -> bool:
195
+ """
196
+ switch to the specified version tag of a local repo
197
+ repo should be in the form of : path/.git
198
+ repo = '../micropython/.git'
199
+ returns None
200
+ """
201
+
202
+ cmd = ["git", "switch", "--detach", tag, "--quiet", "--force"]
203
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
204
+ if not result:
205
+ return False
206
+ # actually a good result
207
+ log.debug(result.stderr)
208
+ return True
209
+
210
+
211
+ def switch_branch(branch: str, repo: Optional[Union[Path, str]] = None) -> bool:
212
+ """
213
+ switch to the specified branch in a local repo"
214
+ repo should be in the form of : path/.git
215
+ repo = '../micropython/.git'
216
+ returns None
217
+ """
218
+ cmd = ["git", "switch", branch, "--quiet", "--force"]
219
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
220
+ if not result:
221
+ return False
222
+ # actually a good result
223
+ log.debug(result.stderr)
224
+ return True
225
+
226
+
227
+ def fetch(repo: Union[Path, str]) -> bool:
228
+ """
229
+ fetches a repo
230
+ repo should be in the form of : path/.git
231
+ repo = '../micropython/.git'
232
+ returns True on success
233
+ """
234
+ if not repo:
235
+ raise NotADirectoryError
236
+
237
+ cmd = ["git", "fetch", "--all", "--tags", "--quiet"]
238
+ result = _run_local_git(cmd, repo=repo, echo_output=False)
239
+ return result.returncode == 0 if result else False
240
+
241
+
242
+ def pull(repo: Union[Path, str], branch: str = "main") -> bool:
243
+ """
244
+ pull a repo origin into main
245
+ repo should be in the form of : path/.git
246
+ repo = '../micropython/.git'
247
+ returns True on success
248
+ """
249
+ if not repo:
250
+ raise NotADirectoryError
251
+ repo = Path(repo)
252
+ # first checkout HEAD
253
+ cmd = ["git", "checkout", branch, "--quiet", "--force"]
254
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
255
+ if not result:
256
+ log.error("error during git checkout main", result)
257
+ return False
258
+
259
+ cmd = ["git", "pull", "origin", branch, "--quiet", "--autostash"]
260
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
261
+ if not result:
262
+ log.error("error durign pull", result)
263
+ return False
264
+ return result.returncode == 0
265
+
266
+
267
+ def get_git_describe(folder: Optional[str] = None):
268
+ """
269
+ Based on MicroPython makeversionhdr.py
270
+ returns : current git tag, commits ,commit hash : "v1.19.1-841-g3446"
271
+ """
272
+ # Note: git describe doesn't work if no tag is available
273
+ try:
274
+ git_describe = subprocess.check_output(
275
+ ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
276
+ stderr=subprocess.STDOUT,
277
+ universal_newlines=True,
278
+ cwd=folder,
279
+ ).strip()
280
+ except subprocess.CalledProcessError as er:
281
+ if er.returncode == 128:
282
+ # git exit code of 128 means no repository found
283
+ return None
284
+ git_describe = ""
285
+ except OSError:
286
+ return None
287
+ # format
288
+ return git_describe
mpflash/vendor/dfu.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # sourcery skip: require-parameter-annotation
2
2
  # sourcery skip: replace-interpolation-with-fstring
3
+ # type: ignore
3
4
  #!/usr/bin/python
4
5
 
5
6
  # Written by Antonio Galea - 2010/11/18
@@ -9,6 +9,8 @@ from functools import lru_cache
9
9
  from loguru import logger as log
10
10
  from packaging.version import parse
11
11
 
12
+ from mpflash.common import GH_CLIENT
13
+
12
14
  V_PREVIEW = "preview"
13
15
  "Latest preview version"
14
16
 
@@ -67,12 +69,12 @@ def clean_version(
67
69
 
68
70
 
69
71
  @lru_cache(maxsize=10)
70
- def micropython_versions(minver: str = "v1.20"):
72
+ def micropython_versions(minver: str = "v1.20", reverse: bool = False):
71
73
  """Get the list of micropython versions from github tags"""
72
74
  try:
73
75
  gh_client = GH_CLIENT
74
76
  repo = gh_client.get_repo("micropython/micropython")
75
- versions = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
77
+ versions = [tag.name for tag in repo.get_tags()]
76
78
  except Exception:
77
79
  versions = [
78
80
  "v9.99.9-preview",
@@ -94,7 +96,9 @@ def micropython_versions(minver: str = "v1.20"):
94
96
  "v1.11",
95
97
  "v1.10",
96
98
  ]
97
- versions = [v for v in versions if parse(v) >= parse(minver)]
99
+ versions = [v for v in versions if parse(v) >= parse(minver)]
100
+ # remove all but the most recent (preview) version
101
+ versions = versions[:1] + [v for v in versions if "preview" not in v]
98
102
  return sorted(versions)
99
103
 
100
104
 
mpflash/worklist.py CHANGED
@@ -3,10 +3,9 @@ from typing import Dict, List, Optional, Tuple
3
3
 
4
4
  from loguru import logger as log
5
5
 
6
- from mpflash.common import FWInfo
6
+ from mpflash.common import FWInfo, filtered_comports
7
7
  from mpflash.errors import MPFlashError
8
8
 
9
- from .config import config
10
9
  from .downloaded import find_downloaded_firmware
11
10
  from .list import show_mcus
12
11
  from .mpboard_id import find_known_board
@@ -60,14 +59,53 @@ def auto_update(
60
59
 
61
60
  # just use the last firmware
62
61
  fw_info = board_firmwares[-1]
63
- log.info(f"Found {target_version} firmware {fw_info['filename']} for {mcu.board} on {mcu.serialport}.")
62
+ log.info(f"Found {target_version} firmware {fw_info.filename} for {mcu.board} on {mcu.serialport}.")
64
63
  wl.append((mcu, fw_info))
65
64
  return wl
66
65
 
67
66
 
67
+ def manual_worklist(
68
+ serial: str,
69
+ *,
70
+ board_id: str,
71
+ version: str,
72
+ fw_folder: Path,
73
+ ) -> WorkList:
74
+ """Create a worklist for a single board specified manually.
75
+
76
+ Args:
77
+ serial (str): Serial port of the board
78
+ board (str): Board_ID
79
+ version (str): Firmware version
80
+ fw_folder (Path): Path to the firmware folder
81
+
82
+ Returns:
83
+ WorkList: List of boards and firmware information to update
84
+ """
85
+ log.trace(f"Manual updating {serial} to {board_id} {version}")
86
+ mcu = MPRemoteBoard(serial)
87
+ # Lookup the matching port and cpu in board_info based in the board name
88
+ try:
89
+ info = find_known_board(board_id)
90
+ mcu.port = info.port
91
+ # need the CPU type for the esptool
92
+ mcu.cpu = info.cpu
93
+ except (LookupError, MPFlashError) as e:
94
+ log.error(f"Board {board_id} not found in board_info.zip")
95
+ log.exception(e)
96
+ return []
97
+ mcu.board = board_id
98
+ firmwares = find_downloaded_firmware(fw_folder=fw_folder, board_id=board_id, version=version, port=mcu.port)
99
+ if not firmwares:
100
+ log.error(f"No firmware found for {mcu.port} {board_id} version {version}")
101
+ return []
102
+ # use the most recent matching firmware
103
+ return [(mcu, firmwares[-1])] # type: ignore
104
+
105
+
68
106
  def single_auto_worklist(
107
+ serial: str,
69
108
  *,
70
- serial_port: str,
71
109
  version: str,
72
110
  fw_folder: Path,
73
111
  ) -> WorkList:
@@ -81,13 +119,16 @@ def single_auto_worklist(
81
119
  Returns:
82
120
  WorkList: List of boards and firmware information to update
83
121
  """
84
- conn_boards = [MPRemoteBoard(serial_port)]
122
+ log.trace(f"Auto updating {serial} to {version}")
123
+ conn_boards = [MPRemoteBoard(serial)]
85
124
  todo = auto_update(conn_boards, version, fw_folder) # type: ignore # List / list
86
125
  show_mcus(conn_boards) # type: ignore
87
126
  return todo
88
127
 
89
128
 
90
- def full_auto_worklist(*, version: str, fw_folder: Path) -> WorkList:
129
+ def full_auto_worklist(
130
+ all_boards: List[MPRemoteBoard], *, include: List[str], ignore: List[str], version: str, fw_folder: Path
131
+ ) -> WorkList:
91
132
  """
92
133
  Create a worklist for all connected micropython boards based on the information retrieved from the board.
93
134
  This allows the firmware version of one or moae boards to be changed without needing to specify the port or board_id manually.
@@ -99,49 +140,31 @@ def full_auto_worklist(*, version: str, fw_folder: Path) -> WorkList:
99
140
  Returns:
100
141
  WorkList: List of boards and firmware information to update
101
142
  """
102
- try:
103
- conn_boards = [
104
- MPRemoteBoard(sp, update=True) for sp in MPRemoteBoard.connected_boards() if sp not in config.ignore_ports
105
- ]
106
- except ConnectionError as e:
107
- log.error(f"Error connecting to boards: {e}")
143
+ log.trace(f"Auto updating all boards to {version}")
144
+ if selected_boards := filter_boards(all_boards, include=include, ignore=ignore):
145
+ return auto_update(selected_boards, version, fw_folder)
146
+ else:
108
147
  return []
109
- return auto_update(conn_boards, version, fw_folder) # type: ignore
110
-
111
-
112
- def manual_worklist(
113
- version: str,
114
- fw_folder: Path,
115
- serial_port: str,
116
- board: str,
117
- # port: str,
118
- ) -> WorkList:
119
- """Create a worklist for a single board specified manually.
120
148
 
121
- Args:
122
- version (str): Firmware version
123
- fw_folder (Path): Path to the firmware folder
124
- serial_port (str): Serial port of the board
125
- board (str): Board name
126
149
 
127
- Returns:
128
- WorkList: List of boards and firmware information to update
129
- """
130
- mcu = MPRemoteBoard(serial_port)
131
- # TODO : Find a way to avoid needing to specify the port
132
- # Lookup the matching port and cpu in board_info based in the board name
150
+ def filter_boards(
151
+ all_boards: List[MPRemoteBoard],
152
+ *,
153
+ include: List[str],
154
+ ignore: List[str],
155
+ ):
133
156
  try:
134
- info = find_known_board(board)
135
- mcu.port = info["port"]
136
- # need the CPU type for the esptool
137
- mcu.cpu = info["cpu"]
138
- except (LookupError, MPFlashError) as e:
139
- log.error(f"Board {board} not found in board_info.json")
140
- return []
141
- mcu.board = board
142
- firmwares = find_downloaded_firmware(fw_folder=fw_folder, board_id=board, version=version, port=mcu.port)
143
- if not firmwares:
144
- log.error(f"No firmware found for {mcu.port} {board} version {version}")
157
+ comports = [
158
+ p.device
159
+ for p in filtered_comports(
160
+ ignore=ignore,
161
+ include=include,
162
+ bluetooth=False,
163
+ )
164
+ ]
165
+ selected_boards = [b for b in all_boards if b.serialport in comports]
166
+ # [MPRemoteBoard(port.device, update=True) for port in comports]
167
+ except ConnectionError as e:
168
+ log.error(f"Error connecting to boards: {e}")
145
169
  return []
146
- # use the most recent matching firmware
147
- return [(mcu, firmwares[-1])] # type: ignore
170
+ return selected_boards # type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mpflash
3
- Version: 0.7.7
3
+ Version: 0.8.1
4
4
  Summary: Flash and download tool for MicroPython firmwares
5
5
  Home-page: https://github.com/Josverl/micropython-stubber/blob/main/src/mpflash/README.md
6
6
  License: MIT
@@ -19,6 +19,7 @@ Classifier: Topic :: Software Development :: Build Tools
19
19
  Requires-Dist: beautifulsoup4 (>=4.12.3,<5.0.0)
20
20
  Requires-Dist: bincopy (>=20.0.0,<21.0.0)
21
21
  Requires-Dist: blkinfo (>=0.2.0,<0.3.0)
22
+ Requires-Dist: cachetools (>=5.3.0,<6.0.0)
22
23
  Requires-Dist: esptool (>=4.7.0,<5.0.0)
23
24
  Requires-Dist: inquirer (>=3.2.4,<4.0.0)
24
25
  Requires-Dist: jsonlines (>=4.0.0,<5.0.0)
@@ -32,20 +33,32 @@ Requires-Dist: psutil (>=5.9.8,<6.0.0)
32
33
  Requires-Dist: pygithub (>=2.1.1,<3.0.0)
33
34
  Requires-Dist: pyusb (>=1.2.1,<2.0.0)
34
35
  Requires-Dist: requests (>=2.31.0,<3.0.0)
35
- Requires-Dist: rich-click (>=1.7.3,<2.0.0)
36
+ Requires-Dist: rich-click (>=1.8.1,<2.0.0)
36
37
  Requires-Dist: tenacity (==8.2.3)
37
38
  Project-URL: Repository, https://github.com/Josverl/micropython-stubber
38
39
  Description-Content-Type: text/markdown
39
40
 
40
41
  # MPFLASH
42
+ [![pypi version](https://badgen.net/pypi/v/mpflash)](https://pypi.org/project/mpflash/)
43
+ [![python versions](https://badgen.net/pypi/python/mpflash)](https://badgen.net/pypi/python/mpflash)
44
+ [![Downloads](https://static.pepy.tech/badge/mpflash)](https://pepy.tech/project/mpflash)
45
+
41
46
 
42
47
  `mpflash` is a command-line tool for working with MicroPython firmware. It provides features to help you flash and update Micropython on one or more .
43
48
 
44
- This tool was initially created to be used in a CI/CD pipeline to automate the process of downloading and flashing MicroPython firmware to multiple boards, but it has been extend with a TUI to me be used for manual downloadig, flashing and development.
49
+ This tool was initially created to be used in a CI/CD pipeline to automate the process of downloading and flashing MicroPython firmware to multiple boards, but it has been extend with a TUI to be used for manual downloadig, flashing and development.
45
50
 
46
- `mpflash` has been tested on Windows x64, Linux X64, but not (yet) macOS.
47
- Tested ports: `rp2`, `samd`, `esp32`, `esp32s3`, `esp32c3`, `esp8266` and `stm32`
51
+ `mpflash` has been tested on:
52
+ - OS: Windows x64, Linux X64, but not (yet) macOS.
53
+ - Micropython (hardware) ports:
54
+ - `rp2`, using `.uf2`, using filecopy (macos not tested yet)
55
+ - `samd`, using ` .uf2`, using filecopy (macos not tested yet)
56
+ - `esp32`, using `.bin`, using esptool,
57
+ - `esp8266`, using `.bin`, using esptool
58
+ - `stm32`, using ` .dfu`, using pydfu
48
59
 
60
+ Not yet implemented: `nrf`, `cc3200`, `mimxrt`
61
+
49
62
  ## Features
50
63
  1. List the connected boards including their firmware details, in a tabular or json format
51
64
  2. Download MicroPython firmware for versions, and matching a specified board or matches your current attached board.
@@ -73,8 +86,7 @@ On Windows this will not be an issue, but on Linux you can use udev rules to gi
73
86
  ## Detailed usage
74
87
  You can list the connected boards using the following command:
75
88
  ```bash
76
- $ mpflash list
77
- D:\MyPython\micropython-stubber> mpflash list
89
+ $> mpflash list
78
90
  Connected boards
79
91
  ┏━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━┓
80
92
  ┃ Serial ┃Family ┃Port ┃Board ┃CPU ┃Version ┃build ┃