mpflash 0.4.2__py3-none-any.whl → 0.6.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/ask_input.py +94 -23
- mpflash/cli_download.py +31 -15
- mpflash/cli_flash.py +53 -113
- mpflash/cli_list.py +2 -60
- mpflash/cli_main.py +7 -3
- mpflash/common.py +2 -126
- mpflash/download.py +15 -3
- mpflash/downloaded.py +108 -0
- mpflash/errors.py +5 -0
- mpflash/flash.py +38 -133
- mpflash/flash_esp.py +10 -15
- mpflash/flash_stm32.py +1 -3
- mpflash/flash_stm32_cube.py +0 -1
- mpflash/flash_stm32_dfu.py +19 -5
- mpflash/list.py +72 -0
- mpflash/mpboard_id/{api.py → __init__.py} +23 -16
- mpflash/mpboard_id/board_id.py +40 -22
- mpflash/mpremoteboard/__init__.py +97 -27
- mpflash/mpremoteboard/runner.py +5 -1
- mpflash/vendor/versions.py +113 -0
- mpflash/worklist.py +147 -0
- {mpflash-0.4.2.dist-info → mpflash-0.6.0.dist-info}/METADATA +5 -5
- mpflash-0.6.0.dist-info/RECORD +40 -0
- mpflash-0.4.2.dist-info/RECORD +0 -35
- /mpflash/{vendored → vendor}/dfu.py +0 -0
- /mpflash/{vendored → vendor}/pydfu.py +0 -0
- /mpflash/{vendored → vendor}/readme.md +0 -0
- {mpflash-0.4.2.dist-info → mpflash-0.6.0.dist-info}/LICENSE +0 -0
- {mpflash-0.4.2.dist-info → mpflash-0.6.0.dist-info}/WHEEL +0 -0
- {mpflash-0.4.2.dist-info → mpflash-0.6.0.dist-info}/entry_points.txt +0 -0
mpflash/list.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from rich import print
|
4
|
+
from rich.progress import track
|
5
|
+
from rich.table import Table
|
6
|
+
|
7
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
+
|
9
|
+
from .config import config
|
10
|
+
from .logger import console
|
11
|
+
|
12
|
+
|
13
|
+
def list_mcus(bluetooth: bool = False):
|
14
|
+
"""
|
15
|
+
Retrieves information about connected microcontroller boards.
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
|
19
|
+
Raises:
|
20
|
+
ConnectionError: If there is an error connecting to a board.
|
21
|
+
"""
|
22
|
+
conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
|
23
|
+
|
24
|
+
for mcu in track(conn_mcus, description="Getting board info", transient=True, update_period=0.1):
|
25
|
+
try:
|
26
|
+
mcu.get_mcu_info()
|
27
|
+
except ConnectionError as e:
|
28
|
+
print(f"Error: {e}")
|
29
|
+
continue
|
30
|
+
return conn_mcus
|
31
|
+
|
32
|
+
|
33
|
+
def show_mcus(
|
34
|
+
conn_mcus: List[MPRemoteBoard],
|
35
|
+
title: str = "Connected boards",
|
36
|
+
refresh: bool = True,
|
37
|
+
): # sourcery skip: extract-duplicate-method
|
38
|
+
"""Show the list of connected boards in a nice table"""
|
39
|
+
table = Table(
|
40
|
+
title=title,
|
41
|
+
title_style="bold",
|
42
|
+
header_style="bold blue",
|
43
|
+
collapse_padding=True,
|
44
|
+
width=110,
|
45
|
+
row_styles=["blue", "yellow"],
|
46
|
+
)
|
47
|
+
table.add_column("Serial", overflow="fold")
|
48
|
+
table.add_column("Family")
|
49
|
+
table.add_column("Port")
|
50
|
+
table.add_column("Board", overflow="fold")
|
51
|
+
# table.add_column("Variant") # TODO: add variant
|
52
|
+
table.add_column("CPU")
|
53
|
+
table.add_column("Version")
|
54
|
+
table.add_column("build", justify="right")
|
55
|
+
|
56
|
+
for mcu in track(conn_mcus, description="Updating board info", transient=True, update_period=0.1):
|
57
|
+
if refresh:
|
58
|
+
try:
|
59
|
+
mcu.get_mcu_info()
|
60
|
+
except ConnectionError:
|
61
|
+
continue
|
62
|
+
table.add_row(
|
63
|
+
mcu.serialport.replace("/dev/", ""),
|
64
|
+
mcu.family,
|
65
|
+
mcu.port,
|
66
|
+
f"{mcu.board}\n{mcu.description}".strip(),
|
67
|
+
# mcu.variant,
|
68
|
+
mcu.cpu,
|
69
|
+
mcu.version,
|
70
|
+
mcu.build,
|
71
|
+
)
|
72
|
+
console.print(table)
|
@@ -1,9 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
Access to the micropython port and board information that is stored in the board_info.json file
|
3
|
+
that is included in the module.
|
4
|
+
|
5
|
+
"""
|
6
|
+
|
1
7
|
import json
|
2
8
|
from functools import lru_cache
|
3
9
|
from pathlib import Path
|
4
10
|
from typing import List, Optional, Tuple, TypedDict, Union
|
5
11
|
|
6
|
-
from mpflash.
|
12
|
+
from mpflash.errors import MPFlashError
|
13
|
+
from mpflash.common import PORT_FWTYPES
|
14
|
+
from mpflash.vendor.versions import clean_version
|
7
15
|
|
8
16
|
|
9
17
|
# Board based on the dataclass Board but changed to TypedDict
|
@@ -22,27 +30,27 @@ class Board(TypedDict):
|
|
22
30
|
|
23
31
|
|
24
32
|
@lru_cache(maxsize=None)
|
25
|
-
def
|
33
|
+
def read_stored_boardinfo() -> List[Board]:
|
26
34
|
"""Reads the board_info.json file and returns the data as a list of Board objects"""
|
27
35
|
with open(Path(__file__).parent / "board_info.json", "r") as file:
|
28
36
|
return json.load(file)
|
29
37
|
|
30
38
|
|
31
|
-
def
|
39
|
+
def local_mp_ports() -> List[str]:
|
32
40
|
# TODO: Filter for Version
|
33
|
-
mp_boards =
|
41
|
+
mp_boards = read_stored_boardinfo()
|
34
42
|
# select the unique ports from info
|
35
43
|
ports = set({board["port"] for board in mp_boards if board["port"] in PORT_FWTYPES.keys()})
|
36
44
|
return sorted(list(ports))
|
37
45
|
|
38
46
|
|
39
|
-
def
|
47
|
+
def get_stored_boards_for_port(port: str, versions: Optional[List[str]] = None):
|
40
48
|
"""
|
41
49
|
Returns a list of boards for the given port and version(s)
|
42
50
|
|
43
51
|
port : str : The Micropython port to filter for
|
44
52
|
versions : List[str] : The Micropython versions to filter for (actual versions required)"""
|
45
|
-
mp_boards =
|
53
|
+
mp_boards = read_stored_boardinfo()
|
46
54
|
|
47
55
|
# filter for 'preview' as they are not in the board_info.json
|
48
56
|
# instead use stable version
|
@@ -60,30 +68,29 @@ def get_mp_boards_for_port(port: str, versions: Optional[List[str]] = None):
|
|
60
68
|
return mp_boards
|
61
69
|
|
62
70
|
|
63
|
-
def
|
71
|
+
def known_stored_boards(port: str, versions: Optional[List[str]] = None) -> List[Tuple[str, str]]:
|
64
72
|
"""
|
65
73
|
Returns a list of tuples with the description and board name for the given port and version
|
66
74
|
|
67
75
|
port : str : The Micropython port to filter for
|
68
76
|
versions : List[str] : The Micropython versions to filter for (actual versions required)
|
69
77
|
"""
|
70
|
-
mp_boards =
|
78
|
+
mp_boards = get_stored_boards_for_port(port, versions)
|
71
79
|
|
72
|
-
boards = set(
|
73
|
-
{(f'{board["description"]} [board["board"]] {board["version"]}', board["board"]) for board in mp_boards}
|
74
|
-
)
|
80
|
+
boards = set({(f'{board["version"]} {board["description"]}', board["board"]) for board in mp_boards})
|
75
81
|
return sorted(list(boards))
|
76
82
|
|
77
83
|
|
78
|
-
|
79
|
-
|
80
|
-
info
|
84
|
+
@lru_cache(maxsize=20)
|
85
|
+
def find_stored_board(board_id: str) -> Board:
|
86
|
+
"""Find the board for the given board_ID or 'board description' and return the board info as a Board object"""
|
87
|
+
info = read_stored_boardinfo()
|
81
88
|
for board_info in info:
|
82
|
-
if board_info["board"]
|
89
|
+
if board_id in (board_info["board"], board_info["description"]):
|
83
90
|
if "cpu" not in board_info or not board_info["cpu"]:
|
84
91
|
if " with " in board_info["description"]:
|
85
92
|
board_info["cpu"] = board_info["description"].split(" with ")[-1]
|
86
93
|
else:
|
87
94
|
board_info["cpu"] = board_info["port"]
|
88
95
|
return board_info
|
89
|
-
raise
|
96
|
+
raise MPFlashError(f"Board {board_id} not found")
|
mpflash/mpboard_id/board_id.py
CHANGED
@@ -2,44 +2,62 @@
|
|
2
2
|
Translate board description to board designator
|
3
3
|
"""
|
4
4
|
|
5
|
+
import functools
|
6
|
+
import json
|
5
7
|
from pathlib import Path
|
6
8
|
from typing import Optional
|
7
9
|
|
10
|
+
from mpflash.errors import MPFlashError
|
11
|
+
from mpflash.vendor.versions import clean_version
|
12
|
+
|
8
13
|
###############################################################################################
|
9
|
-
# TODO : make this a bit nicer
|
10
14
|
HERE = Path(__file__).parent
|
11
15
|
###############################################################################################
|
12
16
|
|
13
17
|
|
14
|
-
def
|
18
|
+
def find_board_id(
|
19
|
+
descr: str, short_descr: str, board_info: Optional[Path] = None, version: str = "stable"
|
20
|
+
) -> Optional[str]:
|
15
21
|
# TODO: use the json file instead of the csv and get the cpu
|
16
|
-
|
22
|
+
boards = find_board_by_description(
|
23
|
+
descr=descr,
|
24
|
+
short_descr=short_descr,
|
25
|
+
board_info=board_info,
|
26
|
+
version=clean_version(version),
|
27
|
+
)
|
28
|
+
return boards[-1]["board"]
|
17
29
|
|
18
30
|
|
19
|
-
|
31
|
+
@functools.lru_cache(maxsize=20)
|
32
|
+
def find_board_by_description(*, descr: str, short_descr: str, version="v1.21.0", board_info: Optional[Path] = None):
|
20
33
|
"""
|
21
34
|
Find the MicroPython BOARD designator based on the description in the firmware
|
22
|
-
using the pre-built board_info.
|
35
|
+
using the pre-built board_info.json file
|
23
36
|
"""
|
24
37
|
if not board_info:
|
25
|
-
board_info = HERE / "board_info.
|
38
|
+
board_info = HERE / "board_info.json"
|
26
39
|
if not board_info.exists():
|
27
40
|
raise FileNotFoundError(f"Board info file not found: {board_info}")
|
28
41
|
|
29
|
-
|
42
|
+
info = _read_board_info(board_info)
|
43
|
+
|
44
|
+
# filter for matching version
|
45
|
+
if version == "preview":
|
46
|
+
# TODO: match last stable
|
47
|
+
version = "v1.22.2"
|
48
|
+
version_matches = [b for b in info if b["version"].startswith(version)]
|
49
|
+
if not version_matches:
|
50
|
+
raise MPFlashError(f"No board info found for version {version}")
|
51
|
+
matches = [b for b in version_matches if b["description"] == descr]
|
52
|
+
if not matches and short_descr:
|
53
|
+
matches = [b for b in version_matches if b["description"] == short_descr]
|
54
|
+
if not matches:
|
55
|
+
raise MPFlashError(f"No board info found for description {descr}")
|
56
|
+
return sorted(matches, key=lambda x: x["version"])
|
57
|
+
|
58
|
+
|
59
|
+
@functools.lru_cache(maxsize=20)
|
60
|
+
def _read_board_info(board_info):
|
30
61
|
with open(board_info, "r") as file:
|
31
|
-
|
32
|
-
|
33
|
-
if not line:
|
34
|
-
break
|
35
|
-
descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip()
|
36
|
-
if descr_ == descr:
|
37
|
-
return board_
|
38
|
-
if short_descr and descr_ == short_descr:
|
39
|
-
if "with" in short_descr:
|
40
|
-
# Good enough - no need to trawl the entire file
|
41
|
-
# info["board"] = board_
|
42
|
-
return board_
|
43
|
-
# good enough if not found in the rest of the file (but slow)
|
44
|
-
short_hit = board_
|
45
|
-
return short_hit or None
|
62
|
+
info = json.load(file)
|
63
|
+
return info
|
@@ -3,16 +3,18 @@ Module to run mpremote commands, and retry on failure or timeout
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import sys
|
6
|
+
import time
|
6
7
|
from pathlib import Path
|
7
8
|
from typing import List, Optional, Union
|
8
9
|
|
9
10
|
import serial.tools.list_ports
|
10
11
|
from loguru import logger as log
|
12
|
+
from rich.progress import track
|
11
13
|
from tenacity import retry, stop_after_attempt, wait_fixed
|
12
14
|
|
13
|
-
from mpflash.
|
14
|
-
|
15
|
-
from .runner import run
|
15
|
+
from mpflash.errors import MPFlashError
|
16
|
+
from mpflash.mpboard_id.board_id import find_board_id
|
17
|
+
from mpflash.mpremoteboard.runner import run
|
16
18
|
|
17
19
|
###############################################################################################
|
18
20
|
# TODO : make this a bit nicer
|
@@ -27,9 +29,15 @@ RETRIES = 3
|
|
27
29
|
class MPRemoteBoard:
|
28
30
|
"""Class to run mpremote commands"""
|
29
31
|
|
30
|
-
def __init__(self, serialport: str = ""):
|
32
|
+
def __init__(self, serialport: str = "", update: bool = False):
|
33
|
+
"""
|
34
|
+
Initialize MPRemoteBoard object.
|
35
|
+
|
36
|
+
Parameters:
|
37
|
+
- serialport (str): The serial port to connect to. Default is an empty string.
|
38
|
+
- update (bool): Whether to update the MCU information. Default is False.
|
39
|
+
"""
|
31
40
|
self.serialport = serialport
|
32
|
-
# self.board = ""
|
33
41
|
self.firmware = {}
|
34
42
|
|
35
43
|
self.connected = False
|
@@ -43,18 +51,49 @@ class MPRemoteBoard:
|
|
43
51
|
self.arch = ""
|
44
52
|
self.mpy = ""
|
45
53
|
self.build = ""
|
54
|
+
if update:
|
55
|
+
self.get_mcu_info()
|
46
56
|
|
47
57
|
def __str__(self):
|
58
|
+
"""
|
59
|
+
Return a string representation of the MPRemoteBoard object.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
- str: The string representation of the object.
|
63
|
+
"""
|
48
64
|
return f"MPRemoteBoard({self.serialport}, {self.family} {self.port}, {self.board}, {self.version})"
|
49
65
|
|
50
66
|
@staticmethod
|
51
|
-
def connected_boards():
|
52
|
-
"""
|
53
|
-
|
54
|
-
|
67
|
+
def connected_boards(bluetooth: bool = False) -> List[str]:
|
68
|
+
"""
|
69
|
+
Get a list of connected boards.
|
70
|
+
|
71
|
+
Parameters:
|
72
|
+
- bluetooth (bool): Whether to include Bluetooth ports. Default is False.
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
- List[str]: A list of connected board ports.
|
76
|
+
"""
|
77
|
+
ports = serial.tools.list_ports.comports()
|
78
|
+
|
79
|
+
if not bluetooth:
|
80
|
+
# filter out bluetooth ports
|
81
|
+
ports = [p for p in ports if "bluetooth" not in p.description.lower()]
|
82
|
+
ports = [p for p in ports if "BTHENUM" not in p.hwid]
|
83
|
+
|
84
|
+
return sorted([p.device for p in ports])
|
85
|
+
|
86
|
+
@retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(1), reraise=True) # type: ignore ## retry_error_cls=ConnectionError,
|
87
|
+
def get_mcu_info(self, timeout: int = 2):
|
88
|
+
"""
|
89
|
+
Get MCU information from the connected board.
|
90
|
+
|
91
|
+
Parameters:
|
92
|
+
- timeout (int): The timeout value in seconds. Default is 2.
|
55
93
|
|
56
|
-
|
57
|
-
|
94
|
+
Raises:
|
95
|
+
- ConnectionError: If failed to get mcu_info for the serial port.
|
96
|
+
"""
|
58
97
|
rc, result = self.run_command(
|
59
98
|
["run", str(HERE / "mpy_fw_info.py")],
|
60
99
|
no_info=True,
|
@@ -76,13 +115,18 @@ class MPRemoteBoard:
|
|
76
115
|
self.description = descr = info["board"]
|
77
116
|
pos = descr.rfind(" with")
|
78
117
|
short_descr = descr[:pos].strip() if pos != -1 else ""
|
79
|
-
if board_name :=
|
118
|
+
if board_name := find_board_id(descr, short_descr):
|
80
119
|
self.board = board_name
|
81
120
|
else:
|
82
121
|
self.board = "UNKNOWN"
|
83
122
|
|
84
123
|
def disconnect(self) -> bool:
|
85
|
-
"""
|
124
|
+
"""
|
125
|
+
Disconnect from a board.
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
- bool: True if successfully disconnected, False otherwise.
|
129
|
+
"""
|
86
130
|
if not self.connected:
|
87
131
|
return True
|
88
132
|
if not self.serialport:
|
@@ -94,7 +138,7 @@ class MPRemoteBoard:
|
|
94
138
|
self.connected = False
|
95
139
|
return result
|
96
140
|
|
97
|
-
@retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(2))
|
141
|
+
@retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(2), reraise=True)
|
98
142
|
def run_command(
|
99
143
|
self,
|
100
144
|
cmd: Union[str, List[str]],
|
@@ -104,21 +148,23 @@ class MPRemoteBoard:
|
|
104
148
|
timeout: int = 60,
|
105
149
|
**kwargs,
|
106
150
|
):
|
107
|
-
"""
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
151
|
+
"""
|
152
|
+
Run mpremote with the given command.
|
153
|
+
|
154
|
+
Parameters:
|
155
|
+
- cmd (Union[str, List[str]]): The command to run, either a string or a list of strings.
|
156
|
+
- log_errors (bool): Whether to log errors. Default is True.
|
157
|
+
- no_info (bool): Whether to skip printing info. Default is False.
|
158
|
+
- timeout (int): The timeout value in seconds. Default is 60.
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
- bool: True if the command succeeded, False otherwise.
|
118
162
|
"""
|
119
163
|
if isinstance(cmd, str):
|
120
164
|
cmd = cmd.split(" ")
|
121
|
-
prefix = [sys.executable, "-m", "mpremote"
|
165
|
+
prefix = [sys.executable, "-m", "mpremote"]
|
166
|
+
if self.serialport:
|
167
|
+
prefix += ["connect", self.serialport]
|
122
168
|
# if connected add resume to keep state between commands
|
123
169
|
if self.connected:
|
124
170
|
prefix += ["resume"]
|
@@ -130,9 +176,33 @@ class MPRemoteBoard:
|
|
130
176
|
|
131
177
|
@retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(1))
|
132
178
|
def mip_install(self, name: str) -> bool:
|
133
|
-
"""
|
179
|
+
"""
|
180
|
+
Install a micropython package.
|
181
|
+
|
182
|
+
Parameters:
|
183
|
+
- name (str): The name of the package to install.
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
- bool: True if the installation succeeded, False otherwise.
|
187
|
+
"""
|
134
188
|
# install createstubs to the board
|
135
189
|
cmd = ["mip", "install", name]
|
136
190
|
result = self.run_command(cmd)[0] == OK
|
137
191
|
self.connected = True
|
138
192
|
return result
|
193
|
+
|
194
|
+
def wait_for_restart(self, timeout: int = 10):
|
195
|
+
"""wait for the board to restart"""
|
196
|
+
for _ in track(
|
197
|
+
range(timeout),
|
198
|
+
description="Waiting for the board to restart",
|
199
|
+
transient=True,
|
200
|
+
get_time=lambda: time.time(),
|
201
|
+
show_speed=False,
|
202
|
+
):
|
203
|
+
time.sleep(1)
|
204
|
+
try:
|
205
|
+
self.get_mcu_info()
|
206
|
+
break
|
207
|
+
except (ConnectionError, MPFlashError):
|
208
|
+
pass
|
mpflash/mpremoteboard/runner.py
CHANGED
@@ -27,6 +27,7 @@ def run(
|
|
27
27
|
log_errors: bool = True,
|
28
28
|
no_info: bool = False,
|
29
29
|
*,
|
30
|
+
log_warnings: bool = False,
|
30
31
|
reset_tags: Optional[LogTagList] = None,
|
31
32
|
error_tags: Optional[LogTagList] = None,
|
32
33
|
warning_tags: Optional[LogTagList] = None,
|
@@ -93,7 +94,8 @@ def run(
|
|
93
94
|
|
94
95
|
def timed_out():
|
95
96
|
proc.kill()
|
96
|
-
|
97
|
+
if log_warnings:
|
98
|
+
log.warning(f"Command {cmd} timed out after {timeout} seconds")
|
97
99
|
|
98
100
|
timer = Timer(timeout, timed_out)
|
99
101
|
try:
|
@@ -123,6 +125,8 @@ def run(
|
|
123
125
|
continue
|
124
126
|
else:
|
125
127
|
if not no_info:
|
128
|
+
if line.startswith(("INFO : ", "WARN : ", "ERROR : ")):
|
129
|
+
line = line[8:].lstrip()
|
126
130
|
log.info(line)
|
127
131
|
if proc.stderr and log_errors:
|
128
132
|
for line in proc.stderr:
|
@@ -0,0 +1,113 @@
|
|
1
|
+
"""
|
2
|
+
#############################################################
|
3
|
+
# Version handling copied from stubber/utils/versions.py
|
4
|
+
#############################################################
|
5
|
+
"""
|
6
|
+
|
7
|
+
from functools import lru_cache
|
8
|
+
|
9
|
+
from loguru import logger as log
|
10
|
+
from packaging.version import parse
|
11
|
+
|
12
|
+
V_PREVIEW = "preview"
|
13
|
+
"Latest preview version"
|
14
|
+
|
15
|
+
SET_PREVIEW = {"preview", "latest", "master"}
|
16
|
+
|
17
|
+
|
18
|
+
def clean_version(
|
19
|
+
version: str,
|
20
|
+
*,
|
21
|
+
build: bool = False,
|
22
|
+
patch: bool = False,
|
23
|
+
commit: bool = False,
|
24
|
+
drop_v: bool = False,
|
25
|
+
flat: bool = False,
|
26
|
+
):
|
27
|
+
"Clean up and transform the many flavours of versions"
|
28
|
+
# 'v1.13.0-103-gb137d064e' --> 'v1.13-103'
|
29
|
+
if version in {"", "-"}:
|
30
|
+
return version
|
31
|
+
if version.lower() == "stable":
|
32
|
+
_v = get_stable_mp_version()
|
33
|
+
if not _v:
|
34
|
+
log.warning("Could not determine the latest stable version")
|
35
|
+
return "stable"
|
36
|
+
version = _v
|
37
|
+
log.trace(f"Using latest stable version: {version}")
|
38
|
+
is_preview = "-preview" in version
|
39
|
+
nibbles = version.split("-")
|
40
|
+
ver_ = nibbles[0].lower().lstrip("v")
|
41
|
+
if not patch and ver_ >= "1.10.0" and ver_ < "1.20.0" and ver_.endswith(".0"):
|
42
|
+
# remove the last ".0" - but only for versions between 1.10 and 1.20 (because)
|
43
|
+
nibbles[0] = nibbles[0][:-2]
|
44
|
+
if len(nibbles) == 1:
|
45
|
+
version = nibbles[0]
|
46
|
+
elif build and not is_preview:
|
47
|
+
version = "-".join(nibbles) if commit else "-".join(nibbles[:-1])
|
48
|
+
else:
|
49
|
+
# version = "-".join((nibbles[0], LATEST))
|
50
|
+
# HACK: this is not always right, but good enough most of the time
|
51
|
+
if is_preview:
|
52
|
+
version = "-".join((nibbles[0], V_PREVIEW))
|
53
|
+
else:
|
54
|
+
version = V_PREVIEW
|
55
|
+
if flat:
|
56
|
+
version = version.strip().replace(".", "_").replace("-", "_")
|
57
|
+
else:
|
58
|
+
version = version.strip().replace("_preview", "-preview").replace("_", ".")
|
59
|
+
|
60
|
+
if drop_v:
|
61
|
+
version = version.lstrip("v")
|
62
|
+
elif not version.startswith("v") and version.lower() not in SET_PREVIEW:
|
63
|
+
version = "v" + version
|
64
|
+
if version in SET_PREVIEW:
|
65
|
+
version = V_PREVIEW
|
66
|
+
return version
|
67
|
+
|
68
|
+
|
69
|
+
@lru_cache(maxsize=10)
|
70
|
+
def micropython_versions(minver: str = "v1.20"):
|
71
|
+
"""Get the list of micropython versions from github tags"""
|
72
|
+
try:
|
73
|
+
gh_client = GH_CLIENT
|
74
|
+
repo = gh_client.get_repo("micropython/micropython")
|
75
|
+
versions = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
|
76
|
+
except Exception:
|
77
|
+
versions = [
|
78
|
+
"v9.99.9-preview",
|
79
|
+
"v1.22.2",
|
80
|
+
"v1.22.1",
|
81
|
+
"v1.22.0",
|
82
|
+
"v1.21.1",
|
83
|
+
"v1.21.0",
|
84
|
+
"v1.20.0",
|
85
|
+
"v1.19.1",
|
86
|
+
"v1.19",
|
87
|
+
"v1.18",
|
88
|
+
"v1.17",
|
89
|
+
"v1.16",
|
90
|
+
"v1.15",
|
91
|
+
"v1.14",
|
92
|
+
"v1.13",
|
93
|
+
"v1.12",
|
94
|
+
"v1.11",
|
95
|
+
"v1.10",
|
96
|
+
]
|
97
|
+
versions = [v for v in versions if parse(v) >= parse(minver)]
|
98
|
+
return sorted(versions)
|
99
|
+
|
100
|
+
|
101
|
+
def get_stable_mp_version() -> str:
|
102
|
+
# read the versions from the git tags
|
103
|
+
all_versions = micropython_versions(minver="v1.17")
|
104
|
+
return [v for v in all_versions if not v.endswith(V_PREVIEW)][-1]
|
105
|
+
|
106
|
+
|
107
|
+
def get_preview_mp_version() -> str:
|
108
|
+
# read the versions from the git tags
|
109
|
+
all_versions = micropython_versions(minver="v1.17")
|
110
|
+
return [v for v in all_versions if v.endswith(V_PREVIEW)][-1]
|
111
|
+
|
112
|
+
|
113
|
+
#############################################################
|