micropython-stubber 1.17.5__py3-none-any.whl → 1.19.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.
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/METADATA +7 -6
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/RECORD +71 -52
- mpflash/README.md +22 -3
- mpflash/libusb_flash.ipynb +203 -0
- mpflash/mpflash/ask_input.py +234 -0
- mpflash/mpflash/cli_download.py +107 -0
- mpflash/mpflash/cli_flash.py +165 -0
- mpflash/mpflash/cli_group.py +41 -8
- mpflash/mpflash/cli_list.py +41 -0
- mpflash/mpflash/cli_main.py +13 -8
- mpflash/mpflash/common.py +33 -122
- mpflash/mpflash/config.py +9 -0
- mpflash/mpflash/{downloader.py → download.py} +112 -120
- mpflash/mpflash/downloaded.py +108 -0
- mpflash/mpflash/errors.py +5 -0
- mpflash/mpflash/flash.py +69 -0
- mpflash/mpflash/flash_esp.py +17 -23
- mpflash/mpflash/flash_stm32.py +16 -113
- mpflash/mpflash/flash_stm32_cube.py +111 -0
- mpflash/mpflash/flash_stm32_dfu.py +101 -0
- mpflash/mpflash/flash_uf2.py +8 -8
- mpflash/mpflash/flash_uf2_linux.py +25 -12
- mpflash/mpflash/flash_uf2_windows.py +24 -12
- mpflash/mpflash/list.py +34 -37
- mpflash/mpflash/logger.py +12 -13
- mpflash/mpflash/mpboard_id/__init__.py +96 -0
- mpflash/mpflash/mpboard_id/board_id.py +63 -0
- mpflash/mpflash/mpboard_id/board_info.csv +2213 -0
- mpflash/mpflash/mpboard_id/board_info.json +19910 -0
- mpflash/mpflash/mpremoteboard/__init__.py +208 -0
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -0
- {stubber/bulk → mpflash/mpflash/mpremoteboard}/runner.py +22 -5
- mpflash/mpflash/vendor/dfu.py +164 -0
- mpflash/mpflash/vendor/pydfu.py +605 -0
- mpflash/mpflash/vendor/readme.md +3 -0
- mpflash/mpflash/vendor/versions.py +113 -0
- mpflash/mpflash/worklist.py +147 -0
- mpflash/poetry.lock +411 -595
- mpflash/pyproject.toml +24 -8
- mpflash/stm32_udev_rules.md +63 -0
- stubber/__init__.py +1 -1
- stubber/basicgit.py +1 -0
- stubber/board/createstubs.py +10 -4
- stubber/board/createstubs_db.py +11 -5
- stubber/board/createstubs_db_min.py +61 -58
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +11 -5
- stubber/board/createstubs_mem_min.py +56 -53
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +54 -51
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/bulk/mcu_stubber.py +9 -5
- stubber/codemod/_partials/db_main.py +14 -25
- stubber/codemod/_partials/lvgl_main.py +2 -2
- stubber/codemod/board.py +10 -3
- stubber/commands/clone_cmd.py +7 -7
- stubber/commands/config_cmd.py +3 -0
- stubber/freeze/get_frozen.py +0 -2
- stubber/publish/candidates.py +1 -1
- stubber/publish/package.py +1 -1
- stubber/publish/pathnames.py +1 -1
- stubber/publish/stubpackage.py +1 -0
- stubber/rst/lookup.py +1 -1
- stubber/tools/manifestfile.py +5 -3
- stubber/utils/config.py +26 -36
- stubber/utils/repos.py +2 -2
- stubber/utils/versions.py +1 -0
- mpflash/mpflash/flasher.py +0 -287
- stubber/bulk/board_id.py +0 -40
- stubber/bulk/mpremoteboard.py +0 -141
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/entry_points.txt +0 -0
- /mpflash/mpflash/{uf2_boardid.py → flash_uf2_boardid.py} +0 -0
mpflash/mpflash/common.py
CHANGED
@@ -1,127 +1,38 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
from typing import TypedDict
|
3
4
|
|
4
|
-
from github import Github
|
5
|
-
from
|
6
|
-
|
5
|
+
from github import Auth, Github
|
6
|
+
from rich.progress import track
|
7
|
+
|
8
|
+
from mpflash.errors import MPFlashError
|
9
|
+
# from mpflash.mpremoteboard import MPRemoteBoard
|
7
10
|
|
8
11
|
PORT_FWTYPES = {
|
9
|
-
"stm32": ".
|
10
|
-
"esp32": ".bin",
|
11
|
-
"esp8266": ".bin",
|
12
|
-
"rp2": ".uf2",
|
13
|
-
"samd": ".uf2",
|
14
|
-
"mimxrt": ".hex",
|
15
|
-
"nrf": ".
|
16
|
-
"renesas-ra": ".hex",
|
12
|
+
"stm32": [".dfu"], # need .dfu for pydfu.py - .hex for cube cli/GUI
|
13
|
+
"esp32": [".bin"],
|
14
|
+
"esp8266": [".bin"],
|
15
|
+
"rp2": [".uf2"],
|
16
|
+
"samd": [".uf2"],
|
17
|
+
"mimxrt": [".hex"],
|
18
|
+
"nrf": [".uf2"],
|
19
|
+
"renesas-ra": [".hex"],
|
17
20
|
}
|
18
21
|
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
version: str
|
35
|
-
|
36
|
-
build: bool = False,
|
37
|
-
patch: bool = False,
|
38
|
-
commit: bool = False,
|
39
|
-
drop_v: bool = False,
|
40
|
-
flat: bool = False,
|
41
|
-
):
|
42
|
-
"Clean up and transform the many flavours of versions"
|
43
|
-
# 'v1.13.0-103-gb137d064e' --> 'v1.13-103'
|
44
|
-
if version in {"", "-"}:
|
45
|
-
return version
|
46
|
-
if version.lower() == "stable":
|
47
|
-
_v = get_stable_version()
|
48
|
-
if not _v:
|
49
|
-
log.warning("Could not determine the latest stable version")
|
50
|
-
return "stable"
|
51
|
-
version = _v
|
52
|
-
log.info(f"Using latest stable version: {version}")
|
53
|
-
is_preview = "-preview" in version
|
54
|
-
nibbles = version.split("-")
|
55
|
-
ver_ = nibbles[0].lower().lstrip("v")
|
56
|
-
if not patch and ver_ >= "1.10.0" and ver_ < "1.20.0" and ver_.endswith(".0"):
|
57
|
-
# remove the last ".0" - but only for versions between 1.10 and 1.20 (because)
|
58
|
-
nibbles[0] = nibbles[0][:-2]
|
59
|
-
if len(nibbles) == 1:
|
60
|
-
version = nibbles[0]
|
61
|
-
elif build and not is_preview:
|
62
|
-
version = "-".join(nibbles) if commit else "-".join(nibbles[:-1])
|
63
|
-
else:
|
64
|
-
# version = "-".join((nibbles[0], LATEST))
|
65
|
-
# HACK: this is not always right, but good enough most of the time
|
66
|
-
if is_preview:
|
67
|
-
version = "-".join((nibbles[0], V_PREVIEW))
|
68
|
-
else:
|
69
|
-
version = V_PREVIEW
|
70
|
-
if flat:
|
71
|
-
version = version.strip().replace(".", "_").replace("-", "_")
|
72
|
-
else:
|
73
|
-
version = version.strip().replace("_preview", "-preview").replace("_", ".")
|
74
|
-
|
75
|
-
if drop_v:
|
76
|
-
version = version.lstrip("v")
|
77
|
-
elif not version.startswith("v") and version.lower() not in SET_PREVIEW:
|
78
|
-
version = "v" + version
|
79
|
-
if version in SET_PREVIEW:
|
80
|
-
version = V_PREVIEW
|
81
|
-
return version
|
82
|
-
|
83
|
-
|
84
|
-
def micropython_versions(minver: str = "v1.9.2"):
|
85
|
-
"""Get the list of micropython versions from github tags"""
|
86
|
-
try:
|
87
|
-
g = Github()
|
88
|
-
_ = 1 / 0
|
89
|
-
repo = g.get_repo("micropython/micropython")
|
90
|
-
versions = [
|
91
|
-
tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)
|
92
|
-
]
|
93
|
-
except Exception:
|
94
|
-
versions = [
|
95
|
-
"v9.99.9-preview",
|
96
|
-
"v1.22.2",
|
97
|
-
"v1.22.1",
|
98
|
-
"v1.22.0",
|
99
|
-
"v1.21.1",
|
100
|
-
"v1.21.0",
|
101
|
-
"v1.20.0",
|
102
|
-
"v1.19.1",
|
103
|
-
"v1.19",
|
104
|
-
"v1.18",
|
105
|
-
"v1.17",
|
106
|
-
"v1.16",
|
107
|
-
"v1.15",
|
108
|
-
"v1.14",
|
109
|
-
"v1.13",
|
110
|
-
"v1.12",
|
111
|
-
"v1.11",
|
112
|
-
"v1.10",
|
113
|
-
"v1.9.4",
|
114
|
-
"v1.9.3",
|
115
|
-
]
|
116
|
-
versions = [v for v in versions if parse(v) >= parse(minver)]
|
117
|
-
return sorted(versions)
|
118
|
-
|
119
|
-
|
120
|
-
def get_stable_version() -> str:
|
121
|
-
# read the versions from the git tags
|
122
|
-
all_versions = micropython_versions(minver="v1.17")
|
123
|
-
stable_version = [v for v in all_versions if not v.endswith(V_PREVIEW)][-1]
|
124
|
-
return stable_version
|
125
|
-
|
126
|
-
|
127
|
-
#############################################################
|
22
|
+
# Token with no permissions to avoid throttling
|
23
|
+
# 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
|
24
|
+
PAT_NO_ACCESS = (
|
25
|
+
"github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
|
26
|
+
)
|
27
|
+
PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
|
28
|
+
GH_CLIENT = Github(auth=Auth.Token(PAT))
|
29
|
+
|
30
|
+
|
31
|
+
class FWInfo(TypedDict):
|
32
|
+
filename: str
|
33
|
+
port: str
|
34
|
+
board: str
|
35
|
+
variant: str
|
36
|
+
preview: bool
|
37
|
+
version: str
|
38
|
+
build: str
|
mpflash/mpflash/config.py
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
"""centralized configuration for mpflash"""
|
2
2
|
|
3
|
+
from pathlib import Path
|
3
4
|
from typing import List
|
4
5
|
|
6
|
+
import platformdirs
|
7
|
+
|
5
8
|
|
6
9
|
class MPtoolConfig:
|
10
|
+
"""Centralized configuration for mpflash"""
|
11
|
+
|
12
|
+
quiet: bool = False
|
13
|
+
verbose: bool = False
|
7
14
|
ignore_ports: List[str] = []
|
15
|
+
interactive: bool = True
|
16
|
+
firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
|
8
17
|
|
9
18
|
|
10
19
|
config = MPtoolConfig()
|
@@ -5,20 +5,24 @@ Uses the micropython.org website to get the available versions and locations to
|
|
5
5
|
|
6
6
|
import functools
|
7
7
|
import itertools
|
8
|
-
import json
|
9
8
|
import re
|
10
9
|
from pathlib import Path
|
11
10
|
from typing import Dict, List, Optional
|
12
11
|
from urllib.parse import urljoin
|
13
12
|
|
13
|
+
# #########################################################################################################
|
14
|
+
# make sure that jsonlines does not mistake the MicroPython ujson for the CPython ujson
|
15
|
+
import jsonlines
|
14
16
|
import requests
|
15
|
-
import rich_click as click
|
16
17
|
from bs4 import BeautifulSoup
|
17
18
|
from loguru import logger as log
|
18
19
|
from rich.progress import track
|
19
20
|
|
20
|
-
from .
|
21
|
-
|
21
|
+
from mpflash.common import PORT_FWTYPES
|
22
|
+
|
23
|
+
jsonlines.ujson = None # type: ignore
|
24
|
+
# #########################################################################################################
|
25
|
+
|
22
26
|
|
23
27
|
MICROPYTHON_ORG_URL = "https://micropython.org/"
|
24
28
|
|
@@ -28,20 +32,6 @@ RE_HASH = r"(.g[0-9a-f]+\.)"
|
|
28
32
|
# regex to extract the version from the firmware filename
|
29
33
|
RE_VERSION_PREVIEW = r"(\d+\.\d+(\.\d+)?(-\w+.\d+)?)"
|
30
34
|
|
31
|
-
# boards we are interested in ( this avoids getting a lot of boards that are not relevant)
|
32
|
-
DEFAULT_BOARDS = [
|
33
|
-
"PYBV11",
|
34
|
-
"ESP8266_GENERIC",
|
35
|
-
"ESP32_GENERIC",
|
36
|
-
"ESP32_GENERIC_S3",
|
37
|
-
"RPI_PICO",
|
38
|
-
"RPI_PICO_W",
|
39
|
-
"ADAFRUIT_QTPY_RP2040",
|
40
|
-
"ARDUINO_NANO_RP2040_CONNECT",
|
41
|
-
"PIMORONI_PICOLIPO_16MB",
|
42
|
-
"SEEED_WIO_TERMINAL",
|
43
|
-
]
|
44
|
-
|
45
35
|
|
46
36
|
# use functools.lru_cache to avoid needing to download pages multiple times
|
47
37
|
@functools.lru_cache(maxsize=500)
|
@@ -56,6 +46,9 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
56
46
|
"""
|
57
47
|
Get the urls to all the board pages listed on this page.
|
58
48
|
Assumes that all links to firmware have "class": "board-card"
|
49
|
+
|
50
|
+
Args:
|
51
|
+
page_url (str): The url of the page to get the board urls from.
|
59
52
|
"""
|
60
53
|
downloads_html = get_page(page_url)
|
61
54
|
soup = BeautifulSoup(downloads_html, "html.parser")
|
@@ -67,8 +60,17 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
67
60
|
return [{"board": board, "url": page_url + board} for board in boards]
|
68
61
|
|
69
62
|
|
70
|
-
def
|
71
|
-
"""
|
63
|
+
def board_firmware_urls(board_url: str, base_url: str, ext: str) -> List[str]:
|
64
|
+
"""
|
65
|
+
Get the urls to all the firmware files for a board.
|
66
|
+
Args:
|
67
|
+
page_url (str): The url of the page to get the board urls from.
|
68
|
+
??? base_url (str): The base url to join the relative urls to.
|
69
|
+
ext (str): The extension of the firmware files to get. (with or withouth leading .)
|
70
|
+
|
71
|
+
the urls are relative urls to the site root
|
72
|
+
|
73
|
+
"""
|
72
74
|
html = get_page(board_url)
|
73
75
|
soup = BeautifulSoup(html, "html.parser")
|
74
76
|
# get all the a tags:
|
@@ -77,9 +79,7 @@ def firmware_list(board_url: str, base_url: str, ext: str) -> List[str]:
|
|
77
79
|
tags = soup.findAll(
|
78
80
|
"a",
|
79
81
|
recursive=True,
|
80
|
-
attrs={
|
81
|
-
"href": re.compile(r"^/resources/firmware/.*\." + ext.lstrip(".") + "$")
|
82
|
-
},
|
82
|
+
attrs={"href": re.compile(r"^/resources/firmware/.*\." + ext.lstrip(".") + "$")},
|
83
83
|
)
|
84
84
|
if "?" in base_url:
|
85
85
|
base_url = base_url.split("?")[0]
|
@@ -94,24 +94,35 @@ FirmwareInfo = Dict[str, str]
|
|
94
94
|
# boards we are interested in ( this avoids getting a lot of boards we don't care about)
|
95
95
|
# The first run takes ~60 seconds to run for 4 ports , all boards
|
96
96
|
# so it makes sense to cache the results and skip boards as soon as possible
|
97
|
-
def get_boards(
|
98
|
-
|
99
|
-
|
97
|
+
def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FirmwareInfo]:
|
98
|
+
"""
|
99
|
+
Retrieves a list of firmware information for the specified ports and boards.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
ports (List[str]): The list of ports to check for firmware.
|
103
|
+
boards (List[str]): The list of boards to retrieve firmware information for.
|
104
|
+
clean (bool): A flag indicating whether to perform a clean retrieval.
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
List[FirmwareInfo]: A list of firmware information for the specified ports and boards.
|
108
|
+
|
109
|
+
"""
|
100
110
|
board_urls: List[FirmwareInfo] = []
|
101
|
-
for port in
|
111
|
+
for port in ports:
|
102
112
|
download_page_url = f"{MICROPYTHON_ORG_URL}download/?port={port}"
|
103
113
|
_urls = get_board_urls(download_page_url)
|
104
114
|
# filter out boards we don't care about
|
105
|
-
_urls = [board for board in _urls if board["board"] in
|
115
|
+
_urls = [board for board in _urls if board["board"] in boards]
|
106
116
|
# add the port to the board urls
|
107
117
|
for board in _urls:
|
108
118
|
board["port"] = port
|
109
119
|
|
110
|
-
for board in track(
|
111
|
-
_urls, description="Checking download pages", transient=True
|
112
|
-
):
|
120
|
+
for board in track(_urls, description=f"Checking {port} download pages", transient=True):
|
113
121
|
# add a board to the list for each firmware found
|
114
|
-
firmwares =
|
122
|
+
firmwares = []
|
123
|
+
for ext in PORT_FWTYPES[port]:
|
124
|
+
firmwares += board_firmware_urls(board["url"], MICROPYTHON_ORG_URL, ext)
|
125
|
+
|
115
126
|
for _url in firmwares:
|
116
127
|
board["firmware"] = _url
|
117
128
|
board["preview"] = "preview" in _url # type: ignore
|
@@ -130,38 +141,35 @@ def get_boards(
|
|
130
141
|
# remove hash from firmware name
|
131
142
|
fname = re.sub(RE_HASH, ".", fname)
|
132
143
|
board["filename"] = fname
|
133
|
-
board["
|
134
|
-
|
135
|
-
if "-v" in board["filename"]
|
136
|
-
else ""
|
137
|
-
)
|
144
|
+
board["ext"] = Path(board["firmware"]).suffix
|
145
|
+
board["variant"] = board["filename"].split("-v")[0] if "-v" in board["filename"] else ""
|
138
146
|
board_urls.append(board.copy())
|
139
147
|
return board_urls
|
140
148
|
|
141
149
|
|
142
|
-
def
|
150
|
+
def key_fw_ver_pre_ext_bld(x: FirmwareInfo):
|
143
151
|
"sorting key for the retrieved board urls"
|
144
|
-
return x["variant"], x["version"], x["preview"], x["build"]
|
152
|
+
return x["variant"], x["version"], x["preview"], x["ext"], x["build"]
|
145
153
|
|
146
154
|
|
147
|
-
def
|
155
|
+
def key_fw_var_pre_ext(x: FirmwareInfo):
|
148
156
|
"Grouping key for the retrieved board urls"
|
149
|
-
return x["variant"], x["preview"]
|
157
|
+
return x["variant"], x["preview"], x["ext"]
|
150
158
|
|
151
159
|
|
152
160
|
def download_firmwares(
|
153
161
|
firmware_folder: Path,
|
154
|
-
|
155
|
-
|
162
|
+
ports: List[str],
|
163
|
+
boards: List[str],
|
164
|
+
versions: Optional[List[str]] = None,
|
156
165
|
*,
|
157
|
-
preview: bool = False,
|
158
166
|
force: bool = False,
|
159
167
|
clean: bool = True,
|
160
168
|
):
|
161
169
|
skipped = downloaded = 0
|
162
|
-
if
|
163
|
-
|
164
|
-
unique_boards = get_firmware_list(
|
170
|
+
if versions is None:
|
171
|
+
versions = []
|
172
|
+
unique_boards = get_firmware_list(ports, boards, versions, clean)
|
165
173
|
|
166
174
|
for b in unique_boards:
|
167
175
|
log.debug(b["filename"])
|
@@ -171,9 +179,7 @@ def download_firmwares(
|
|
171
179
|
|
172
180
|
firmware_folder.mkdir(exist_ok=True)
|
173
181
|
|
174
|
-
with open(
|
175
|
-
firmware_folder / "firmware.jsonl", "a", encoding="utf-8", buffering=1
|
176
|
-
) as f_jsonl:
|
182
|
+
with jsonlines.open(firmware_folder / "firmware.jsonl", "a") as writer:
|
177
183
|
for board in unique_boards:
|
178
184
|
filename = firmware_folder / board["port"] / board["filename"]
|
179
185
|
filename.parent.mkdir(exist_ok=True)
|
@@ -181,7 +187,8 @@ def download_firmwares(
|
|
181
187
|
skipped += 1
|
182
188
|
log.debug(f" {filename} already exists, skip download")
|
183
189
|
continue
|
184
|
-
log.info(f"Downloading {board['firmware']}
|
190
|
+
log.info(f"Downloading {board['firmware']}")
|
191
|
+
log.info(f" to {filename}")
|
185
192
|
try:
|
186
193
|
r = requests.get(board["firmware"], allow_redirects=True)
|
187
194
|
with open(filename, "wb") as fw:
|
@@ -190,34 +197,41 @@ def download_firmwares(
|
|
190
197
|
except requests.RequestException as e:
|
191
198
|
log.exception(e)
|
192
199
|
continue
|
193
|
-
|
194
|
-
json_str = json.dumps(board) + "\n"
|
195
|
-
f_jsonl.write(json_str)
|
200
|
+
writer.write(board)
|
196
201
|
downloaded += 1
|
197
202
|
log.info(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
|
198
203
|
|
199
204
|
|
200
|
-
def get_firmware_list(
|
201
|
-
|
202
|
-
|
203
|
-
log.trace("Checking MicroPython download pages")
|
205
|
+
def get_firmware_list(ports: List[str], boards: List[str], versions: List[str], clean: bool):
|
206
|
+
"""
|
207
|
+
Retrieves a list of unique firmware information based on the specified ports, boards, versions, and clean flag.
|
204
208
|
|
205
|
-
|
206
|
-
|
207
|
-
|
209
|
+
Args:
|
210
|
+
ports : The list of ports to check for firmware.
|
211
|
+
boards : The list of boards to filter the firmware by.
|
212
|
+
versions : The list of versions to filter the firmware by.
|
213
|
+
clean : A flag indicating whether to perform a clean check.
|
214
|
+
|
215
|
+
Returns:
|
216
|
+
List[FirmwareInfo]: A list of unique firmware information.
|
217
|
+
|
218
|
+
"""
|
219
|
+
|
220
|
+
log.trace("Checking MicroPython download pages")
|
221
|
+
preview = "preview" in versions
|
222
|
+
board_urls = sorted(get_boards(ports, boards, clean), key=key_fw_ver_pre_ext_bld)
|
208
223
|
|
209
224
|
log.debug(f"Total {len(board_urls)} firmwares")
|
210
225
|
relevant = [
|
211
226
|
board
|
212
227
|
for board in board_urls
|
213
|
-
if board["board"] in
|
214
|
-
and (board["version"] in version_list or board["preview"] and preview)
|
228
|
+
if board["board"] in boards and (board["version"] in versions or board["preview"] and preview)
|
215
229
|
# and b["port"] in ["esp32", "rp2"]
|
216
230
|
]
|
217
231
|
log.debug(f"Matching firmwares: {len(relevant)}")
|
218
232
|
# select the unique boards
|
219
233
|
unique_boards: List[FirmwareInfo] = []
|
220
|
-
for _, g in itertools.groupby(relevant, key=
|
234
|
+
for _, g in itertools.groupby(relevant, key=key_fw_var_pre_ext):
|
221
235
|
# list is aleady sorted by build so we can just get the last item
|
222
236
|
sub_list = list(g)
|
223
237
|
unique_boards.append(sub_list[-1])
|
@@ -225,61 +239,39 @@ def get_firmware_list(
|
|
225
239
|
return unique_boards
|
226
240
|
|
227
241
|
|
228
|
-
@cli.command(
|
229
|
-
"download",
|
230
|
-
help="Download MicroPython firmware for specific ports, boards and versions.",
|
231
|
-
)
|
232
|
-
@click.option(
|
233
|
-
"--destination",
|
234
|
-
"-d",
|
235
|
-
type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
|
236
|
-
default="./firmware",
|
237
|
-
show_default=True,
|
238
|
-
help="The folder to download the firmware to.",
|
239
|
-
)
|
240
|
-
@click.option(
|
241
|
-
"--version",
|
242
|
-
"-v",
|
243
|
-
"versions",
|
244
|
-
multiple=True,
|
245
|
-
help="The version of MicroPython to to download. Use 'preview' to include preview versions.",
|
246
|
-
show_default=True,
|
247
|
-
default=["stable"],
|
248
|
-
)
|
249
|
-
@click.option(
|
250
|
-
"--board",
|
251
|
-
"-b",
|
252
|
-
"boards",
|
253
|
-
multiple=True,
|
254
|
-
show_default=True,
|
255
|
-
help="The board(s) to download the firmware for.", # Use '--board all' to download all boards.",
|
256
|
-
)
|
257
|
-
@click.option(
|
258
|
-
"--clean/--no-clean",
|
259
|
-
default=True,
|
260
|
-
show_default=True,
|
261
|
-
help="""Remove dates and hashes from the downloaded firmware filenames.""",
|
262
|
-
)
|
263
|
-
@click.option(
|
264
|
-
"--force",
|
265
|
-
default=False,
|
266
|
-
is_flag=True,
|
267
|
-
help="""Force download of firmware even if it already exists.""",
|
268
|
-
show_default=True,
|
269
|
-
)
|
270
242
|
def download(
|
271
|
-
destination: Path,
|
243
|
+
destination: Path,
|
244
|
+
ports: List[str],
|
245
|
+
boards: List[str],
|
246
|
+
versions: List[str],
|
247
|
+
force: bool,
|
248
|
+
clean: bool,
|
272
249
|
):
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
250
|
+
"""
|
251
|
+
Downloads firmware files based on the specified destination, ports, boards, versions, force flag, and clean flag.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
destination : The destination folder to save the downloaded firmware files.
|
255
|
+
ports : The list of ports to check for firmware.
|
256
|
+
boards : The list of boards to download firmware for.
|
257
|
+
versions : The list of versions to download firmware for.
|
258
|
+
force : A flag indicating whether to force the download even if the firmware file already exists.
|
259
|
+
clean : A flag indicating whether to perform a clean download.
|
260
|
+
|
261
|
+
Returns:
|
262
|
+
None
|
263
|
+
|
264
|
+
Raises:
|
265
|
+
SystemExit: If no boards are found or specified.
|
266
|
+
|
267
|
+
"""
|
268
|
+
if not boards:
|
269
|
+
log.critical("No boards found, please connect a board or specify boards to download firmware for.")
|
270
|
+
exit(1)
|
271
|
+
# versions = [clean_version(v, drop_v=True) for v in versions] # remove leading v from version
|
272
|
+
try:
|
273
|
+
destination.mkdir(exist_ok=True, parents=True)
|
274
|
+
except (PermissionError, FileNotFoundError) as e:
|
275
|
+
log.critical(f"Could not create folder {destination}\n{e}")
|
276
|
+
exit(1)
|
277
|
+
download_firmwares(destination, ports, boards, versions, force=force, clean=clean)
|
@@ -0,0 +1,108 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Dict, List, Optional
|
3
|
+
|
4
|
+
import jsonlines
|
5
|
+
from loguru import logger as log
|
6
|
+
|
7
|
+
from mpflash.common import FWInfo
|
8
|
+
from mpflash.vendor.versions import clean_version
|
9
|
+
|
10
|
+
from .config import config
|
11
|
+
|
12
|
+
|
13
|
+
# #########################################################################################################
|
14
|
+
def downloaded_firmwares(fw_folder: Path) -> List[FWInfo]:
|
15
|
+
"""Load a list of locally downloaded firmwares from the jsonl file"""
|
16
|
+
firmwares: List[FWInfo] = []
|
17
|
+
try:
|
18
|
+
with jsonlines.open(fw_folder / "firmware.jsonl") as reader:
|
19
|
+
firmwares.extend(iter(reader))
|
20
|
+
except FileNotFoundError:
|
21
|
+
log.error(f"No firmware.jsonl found in {fw_folder}")
|
22
|
+
# sort by filename
|
23
|
+
firmwares.sort(key=lambda x: x["filename"])
|
24
|
+
return firmwares
|
25
|
+
|
26
|
+
|
27
|
+
def find_downloaded_firmware(
|
28
|
+
*,
|
29
|
+
board_id: str,
|
30
|
+
version: str = "",
|
31
|
+
port: str = "",
|
32
|
+
variants: bool = False,
|
33
|
+
fw_folder: Optional[Path] = None,
|
34
|
+
trie: int = 1,
|
35
|
+
selector: Optional[Dict[str, str]] = None,
|
36
|
+
) -> List[FWInfo]:
|
37
|
+
if selector is None:
|
38
|
+
selector = {}
|
39
|
+
fw_folder = fw_folder or config.firmware_folder
|
40
|
+
# Use the information in firmwares.jsonl to find the firmware file
|
41
|
+
fw_list = downloaded_firmwares(fw_folder)
|
42
|
+
if not fw_list:
|
43
|
+
log.error("No firmware files found. Please download the firmware first.")
|
44
|
+
return []
|
45
|
+
# filter by version
|
46
|
+
version = clean_version(version, drop_v=True)
|
47
|
+
fw_list = filter_downloaded_fwlist(fw_list, board_id, version, port, variants, selector)
|
48
|
+
|
49
|
+
if not fw_list and trie < 3:
|
50
|
+
log.info(f"Try ({trie+1}) to find a firmware for the board {board_id}")
|
51
|
+
if trie == 1:
|
52
|
+
# ESP board naming conventions have changed by adding a PORT refix
|
53
|
+
if port.startswith("esp") and not board_id.startswith(port.upper()):
|
54
|
+
board_id = f"{port.upper()}_{board_id}"
|
55
|
+
# RP2 board naming conventions have changed by adding a _RPIprefix
|
56
|
+
if port == "rp2" and not board_id.startswith("RPI_"):
|
57
|
+
board_id = f"RPI_{board_id}"
|
58
|
+
elif trie == 2:
|
59
|
+
board_id = board_id.replace("_", "-")
|
60
|
+
|
61
|
+
fw_list = find_downloaded_firmware(
|
62
|
+
fw_folder=fw_folder,
|
63
|
+
board_id=board_id,
|
64
|
+
version=version,
|
65
|
+
port=port,
|
66
|
+
trie=trie + 1,
|
67
|
+
selector=selector,
|
68
|
+
)
|
69
|
+
# hope we have a match now for the board
|
70
|
+
# sort by filename
|
71
|
+
fw_list.sort(key=lambda x: x["filename"])
|
72
|
+
return fw_list
|
73
|
+
|
74
|
+
|
75
|
+
def filter_downloaded_fwlist(
|
76
|
+
fw_list: List[FWInfo],
|
77
|
+
board_id: str,
|
78
|
+
version: str,
|
79
|
+
port: str,
|
80
|
+
# preview: bool,
|
81
|
+
variants: bool,
|
82
|
+
selector: dict,
|
83
|
+
) -> List[FWInfo]:
|
84
|
+
"""Filter the downloaded firmware list based on the provided parameters"""
|
85
|
+
if "preview" in version:
|
86
|
+
# never get a preview for an older version
|
87
|
+
fw_list = [fw for fw in fw_list if fw["preview"]]
|
88
|
+
else:
|
89
|
+
fw_list = [fw for fw in fw_list if fw["version"] == version]
|
90
|
+
|
91
|
+
# filter by port
|
92
|
+
if port:
|
93
|
+
fw_list = [fw for fw in fw_list if fw["port"] == port]
|
94
|
+
|
95
|
+
if board_id:
|
96
|
+
if variants:
|
97
|
+
# any variant of this board_id
|
98
|
+
fw_list = [fw for fw in fw_list if fw["board"] == board_id]
|
99
|
+
else:
|
100
|
+
# the firmware variant should match exactly the board_id
|
101
|
+
fw_list = [fw for fw in fw_list if fw["variant"] == board_id]
|
102
|
+
if selector and port in selector:
|
103
|
+
fw_list = [fw for fw in fw_list if fw["filename"].endswith(selector[port])]
|
104
|
+
return fw_list
|
105
|
+
|
106
|
+
|
107
|
+
# #########################################################################################################
|
108
|
+
#
|