micropython-stubber 1.20.5__py3-none-any.whl → 1.23.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.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/LICENSE +30 -30
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/METADATA +1 -1
- micropython_stubber-1.23.0.dist-info/RECORD +159 -0
- mpflash/README.md +184 -184
- mpflash/libusb_flash.ipynb +203 -203
- mpflash/mpflash/add_firmware.py +98 -98
- mpflash/mpflash/ask_input.py +236 -236
- mpflash/mpflash/bootloader/__init__.py +37 -36
- mpflash/mpflash/bootloader/manual.py +102 -102
- mpflash/mpflash/bootloader/micropython.py +10 -10
- mpflash/mpflash/bootloader/touch1200.py +45 -45
- mpflash/mpflash/cli_download.py +129 -129
- mpflash/mpflash/cli_flash.py +219 -219
- mpflash/mpflash/cli_group.py +98 -98
- mpflash/mpflash/cli_list.py +81 -81
- mpflash/mpflash/cli_main.py +41 -41
- mpflash/mpflash/common.py +164 -164
- mpflash/mpflash/config.py +43 -47
- mpflash/mpflash/connected.py +74 -74
- mpflash/mpflash/download.py +360 -360
- mpflash/mpflash/downloaded.py +130 -129
- mpflash/mpflash/errors.py +9 -9
- mpflash/mpflash/flash.py +55 -52
- mpflash/mpflash/flash_esp.py +59 -59
- mpflash/mpflash/flash_stm32.py +18 -24
- mpflash/mpflash/flash_stm32_cube.py +111 -111
- mpflash/mpflash/flash_stm32_dfu.py +104 -101
- mpflash/mpflash/flash_uf2.py +89 -67
- mpflash/mpflash/flash_uf2_boardid.py +15 -15
- mpflash/mpflash/flash_uf2_linux.py +129 -123
- mpflash/mpflash/flash_uf2_macos.py +37 -34
- mpflash/mpflash/flash_uf2_windows.py +38 -34
- mpflash/mpflash/list.py +89 -89
- mpflash/mpflash/logger.py +41 -41
- mpflash/mpflash/mpboard_id/__init__.py +93 -93
- mpflash/mpflash/mpboard_id/add_boards.py +255 -255
- mpflash/mpflash/mpboard_id/board.py +37 -37
- mpflash/mpflash/mpboard_id/board_id.py +86 -86
- mpflash/mpflash/mpboard_id/store.py +43 -43
- mpflash/mpflash/mpremoteboard/__init__.py +226 -221
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -141
- mpflash/mpflash/mpremoteboard/runner.py +140 -140
- mpflash/mpflash/uf2disk.py +12 -12
- mpflash/mpflash/vendor/basicgit.py +288 -288
- mpflash/mpflash/vendor/click_aliases.py +91 -91
- mpflash/mpflash/vendor/dfu.py +165 -165
- mpflash/mpflash/vendor/pydfu.py +605 -605
- mpflash/mpflash/vendor/readme.md +2 -2
- mpflash/mpflash/vendor/versions.py +119 -117
- mpflash/mpflash/worklist.py +171 -170
- mpflash/poetry.lock +1588 -1588
- mpflash/pyproject.toml +64 -60
- mpflash/stm32_udev_rules.md +62 -62
- stubber/__init__.py +3 -3
- stubber/basicgit.py +294 -288
- stubber/board/board_info.csv +193 -193
- stubber/board/boot.py +34 -34
- stubber/board/createstubs.py +986 -986
- stubber/board/createstubs_db.py +825 -825
- stubber/board/createstubs_db_min.py +331 -331
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_lvgl.py +741 -741
- stubber/board/createstubs_lvgl_min.py +741 -741
- stubber/board/createstubs_mem.py +766 -766
- stubber/board/createstubs_mem_min.py +306 -306
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +294 -294
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/fw_info.py +141 -141
- stubber/board/info.py +183 -183
- stubber/board/main.py +19 -19
- stubber/board/modulelist.txt +247 -247
- stubber/board/pyrightconfig.json +34 -34
- stubber/bulk/mcu_stubber.py +454 -454
- stubber/codemod/_partials/__init__.py +48 -48
- stubber/codemod/_partials/db_main.py +147 -147
- stubber/codemod/_partials/lvgl_main.py +77 -77
- stubber/codemod/_partials/modules_reader.py +80 -80
- stubber/codemod/add_comment.py +53 -53
- stubber/codemod/add_method.py +65 -65
- stubber/codemod/board.py +317 -317
- stubber/codemod/enrich.py +145 -145
- stubber/codemod/merge_docstub.py +284 -284
- stubber/codemod/modify_list.py +54 -54
- stubber/codemod/utils.py +57 -57
- stubber/commands/build_cmd.py +94 -94
- stubber/commands/cli.py +55 -51
- stubber/commands/clone_cmd.py +77 -66
- stubber/commands/config_cmd.py +29 -29
- stubber/commands/enrich_folder_cmd.py +71 -70
- stubber/commands/get_core_cmd.py +71 -69
- stubber/commands/get_docstubs_cmd.py +89 -87
- stubber/commands/get_frozen_cmd.py +114 -112
- stubber/commands/get_mcu_cmd.py +61 -56
- stubber/commands/merge_cmd.py +67 -66
- stubber/commands/publish_cmd.py +119 -119
- stubber/commands/stub_cmd.py +31 -30
- stubber/commands/switch_cmd.py +62 -54
- stubber/commands/variants_cmd.py +49 -48
- stubber/cst_transformer.py +178 -178
- stubber/data/board_info.csv +193 -193
- stubber/data/board_info.json +1729 -1729
- stubber/data/micropython_tags.csv +15 -15
- stubber/data/requirements-core-micropython.txt +38 -38
- stubber/data/requirements-core-pycopy.txt +39 -39
- stubber/downloader.py +36 -36
- stubber/freeze/common.py +68 -68
- stubber/freeze/freeze_folder.py +69 -69
- stubber/freeze/freeze_manifest_2.py +113 -113
- stubber/freeze/get_frozen.py +127 -127
- stubber/get_cpython.py +101 -101
- stubber/get_lobo.py +59 -59
- stubber/minify.py +418 -418
- stubber/publish/bump.py +86 -86
- stubber/publish/candidates.py +262 -262
- stubber/publish/database.py +18 -18
- stubber/publish/defaults.py +45 -45
- stubber/publish/enums.py +24 -24
- stubber/publish/helpers.py +29 -29
- stubber/publish/merge_docstubs.py +130 -130
- stubber/publish/missing_class_methods.py +49 -49
- stubber/publish/package.py +146 -146
- stubber/publish/pathnames.py +51 -51
- stubber/publish/publish.py +120 -120
- stubber/publish/pypi.py +38 -38
- stubber/publish/stubpackage.py +1029 -1029
- stubber/rst/__init__.py +9 -9
- stubber/rst/classsort.py +77 -77
- stubber/rst/lookup.py +530 -530
- stubber/rst/output_dict.py +401 -401
- stubber/rst/reader.py +822 -822
- stubber/rst/report_return.py +69 -69
- stubber/rst/rst_utils.py +540 -540
- stubber/stubber.py +38 -38
- stubber/stubs_from_docs.py +90 -90
- stubber/tools/manifestfile.py +655 -610
- stubber/tools/readme.md +7 -6
- stubber/update_fallback.py +117 -117
- stubber/update_module_list.py +123 -123
- stubber/utils/__init__.py +5 -5
- stubber/utils/config.py +127 -127
- stubber/utils/makeversionhdr.py +54 -54
- stubber/utils/manifest.py +92 -92
- stubber/utils/post.py +79 -79
- stubber/utils/repos.py +157 -154
- stubber/utils/stubmaker.py +139 -139
- stubber/utils/typed_config_toml.py +77 -77
- stubber/utils/versions.py +128 -120
- stubber/variants.py +106 -106
- micropython_stubber-1.20.5.dist-info/RECORD +0 -159
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/entry_points.txt +0 -0
stubber/bulk/mcu_stubber.py
CHANGED
@@ -1,454 +1,454 @@
|
|
1
|
-
"""
|
2
|
-
This script creates stubs on and for a connected micropython MCU board.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import json
|
6
|
-
import shutil
|
7
|
-
import sys
|
8
|
-
import time
|
9
|
-
from enum import Enum
|
10
|
-
from pathlib import Path
|
11
|
-
from tempfile import mkdtemp
|
12
|
-
from typing import List, Optional, Tuple
|
13
|
-
|
14
|
-
from loguru import logger as log
|
15
|
-
from rich.console import Console
|
16
|
-
from rich.table import Table
|
17
|
-
from tenacity import retry, stop_after_attempt, wait_fixed
|
18
|
-
|
19
|
-
from mpflash.mpremoteboard import ERROR, OK, MPRemoteBoard
|
20
|
-
from stubber import utils
|
21
|
-
from stubber.publish.merge_docstubs import merge_all_docstubs
|
22
|
-
from stubber.publish.pathnames import board_folder_name
|
23
|
-
from stubber.publish.publish import build_multiple
|
24
|
-
from stubber.utils.config import CONFIG
|
25
|
-
|
26
|
-
HERE = Path(__file__).parent
|
27
|
-
###############################################################################################
|
28
|
-
# TODO: promote to cmdline params
|
29
|
-
LOCAL_FILES = False
|
30
|
-
reset_before = True
|
31
|
-
TESTING = False
|
32
|
-
###############################################################################################
|
33
|
-
|
34
|
-
|
35
|
-
###############################################################################################
|
36
|
-
|
37
|
-
|
38
|
-
class Variant(str, Enum):
|
39
|
-
"""Variants to generate stubs on a MCU"""
|
40
|
-
|
41
|
-
full = "full"
|
42
|
-
mem = "mem"
|
43
|
-
db = "db"
|
44
|
-
|
45
|
-
|
46
|
-
class Form(str, Enum):
|
47
|
-
"""Optimization forms of scripts"""
|
48
|
-
|
49
|
-
py = "py"
|
50
|
-
min = "min"
|
51
|
-
mpy = "mpy"
|
52
|
-
|
53
|
-
|
54
|
-
def copy_createstubs_to_board(board: MPRemoteBoard, variant: Variant, form: Form) -> bool:
|
55
|
-
# sourcery skip: assign-if-exp, boolean-if-exp-identity, remove-unnecessary-cast
|
56
|
-
"""Copy createstubs to the board"""
|
57
|
-
# copy createstubs.py to the destination folder
|
58
|
-
origin = "./src/stubber/board"
|
59
|
-
|
60
|
-
_py = [
|
61
|
-
"rm :lib/createstubs.mpy",
|
62
|
-
"rm :lib/createstubs_mem.mpy",
|
63
|
-
"rm :lib/createstubs_db.mpy",
|
64
|
-
"rm :lib/createstubs.py",
|
65
|
-
"rm :lib/createstubs_mem.py",
|
66
|
-
"rm :lib/createstubs_db.py",
|
67
|
-
f"cp {origin}/createstubs.py :lib/createstubs.py",
|
68
|
-
f"cp {origin}/createstubs_mem.py :lib/createstubs_mem.py",
|
69
|
-
f"cp {origin}/createstubs_db.py :lib/createstubs_db.py",
|
70
|
-
]
|
71
|
-
|
72
|
-
# copy createstubs*_min.py to the destination folder
|
73
|
-
_min = [
|
74
|
-
f"cp {origin}/createstubs_min.py :lib/createstubs.py",
|
75
|
-
f"cp {origin}/createstubs_mem_min.py :lib/createstubs_mem.py",
|
76
|
-
f"cp {origin}/createstubs_db_min.py :lib/createstubs_db.py",
|
77
|
-
]
|
78
|
-
# copy createstubs*_mpy.mpy to the destination folder
|
79
|
-
_mpy = [
|
80
|
-
"rm :lib/createstubs.py",
|
81
|
-
"rm :lib/createstubs_mem.py",
|
82
|
-
"rm :lib/createstubs_db.py",
|
83
|
-
f"cp {origin}/createstubs_mpy.mpy :lib/createstubs.mpy",
|
84
|
-
f"cp {origin}/createstubs_mem_mpy.mpy :lib/createstubs_mem.mpy",
|
85
|
-
f"cp {origin}/createstubs_db_mpy.mpy :lib/createstubs_db.mpy",
|
86
|
-
]
|
87
|
-
|
88
|
-
_lib = [
|
89
|
-
[
|
90
|
-
"exec",
|
91
|
-
"import os;os.mkdir('lib') if not ('lib' in os.listdir()) else print('folder lib already exists')",
|
92
|
-
]
|
93
|
-
]
|
94
|
-
|
95
|
-
_get_ready = [
|
96
|
-
"rm :modulelist.done",
|
97
|
-
"rm :no_auto_stubber.txt",
|
98
|
-
f"cp {origin}/modulelist.txt :lib/modulelist.txt",
|
99
|
-
]
|
100
|
-
if form == Form.py:
|
101
|
-
do = _lib + _py + _get_ready
|
102
|
-
elif form == Form.min:
|
103
|
-
do = _lib + _min + _get_ready
|
104
|
-
else:
|
105
|
-
do = _lib + _mpy + _get_ready
|
106
|
-
|
107
|
-
# assume all ok, unless one is not ok
|
108
|
-
for cmd in do:
|
109
|
-
if isinstance(cmd, str) and cmd.startswith("rm "):
|
110
|
-
log_errors = False
|
111
|
-
else:
|
112
|
-
log_errors = True
|
113
|
-
rc, _ = board.run_command(cmd, log_errors=log_errors)
|
114
|
-
if rc != OK and "rm" not in cmd:
|
115
|
-
log.error(f"Error during copy createstubs running command: {cmd}")
|
116
|
-
return False
|
117
|
-
return True
|
118
|
-
|
119
|
-
|
120
|
-
@retry(stop=stop_after_attempt(4), wait=wait_fixed(2))
|
121
|
-
def hard_reset(board: MPRemoteBoard) -> bool:
|
122
|
-
"""Reset the board"""
|
123
|
-
# do not run "exec", "import machine;machine.reset()" as it will hang an esp32
|
124
|
-
rc, _ = board.run_command(["reset"], timeout=5)
|
125
|
-
board.connected = False
|
126
|
-
return rc == OK
|
127
|
-
|
128
|
-
|
129
|
-
@retry(stop=stop_after_attempt(10), wait=wait_fixed(15))
|
130
|
-
def run_createstubs(dest: Path, mcu: MPRemoteBoard, variant: Variant = Variant.db):
|
131
|
-
"""
|
132
|
-
Run a createstubs[variant] on the provided board.
|
133
|
-
Retry running the command up to 10 times, with a 15 second timeout between retries.
|
134
|
-
this should allow for the boards with little memory to complete even if they run out of memory.
|
135
|
-
"""
|
136
|
-
# add the lib folder to the path
|
137
|
-
cmd_path = [
|
138
|
-
"exec",
|
139
|
-
'import sys;sys.path.append("/lib") if "/lib" not in sys.path else "/lib already in path"',
|
140
|
-
]
|
141
|
-
mcu.run_command(cmd_path, timeout=5)
|
142
|
-
|
143
|
-
if reset_before:
|
144
|
-
log.info(f"Resetting {mcu.serialport} {mcu.description}")
|
145
|
-
mcu.run_command("reset", timeout=5)
|
146
|
-
time.sleep(2)
|
147
|
-
|
148
|
-
log.info(f"Running createstubs {variant.value} on {mcu.serialport} {mcu.description} using temp path: {dest}")
|
149
|
-
cmd = build_cmd(dest, variant)
|
150
|
-
log.info(f"Running : mpremote {' '.join(cmd)}")
|
151
|
-
mcu.run_command.retry.wait = wait_fixed(15)
|
152
|
-
# some boards need 2-3 minutes to run createstubs - so increase the default timeout
|
153
|
-
# esp32s3 > 240 seconds with mounted fs
|
154
|
-
# but slows down esp8266 restarts so keep that to 90 seconds
|
155
|
-
timeout = 90 if mcu.port == "esp8266" else 6 * 60 # type: ignore
|
156
|
-
rc, out = mcu.run_command(cmd, timeout=timeout)
|
157
|
-
# check last line for exception or error and raise that if found
|
158
|
-
if rc != OK and out and ":" in out[-1] and not out[-1].startswith("INFO") and not out[-1].startswith("WARN"):
|
159
|
-
log.warning(f"createstubs: {out[-1]}")
|
160
|
-
raise RuntimeError(out[-1]) from eval(out[-1].split(":")[0])
|
161
|
-
|
162
|
-
if rc != OK and variant == Variant.db:
|
163
|
-
# assume createstubs ran out of memory and try again
|
164
|
-
raise MemoryError("Memory error, try again")
|
165
|
-
return rc, out
|
166
|
-
|
167
|
-
|
168
|
-
def build_cmd(dest: Path, variant: Variant = Variant.db):
|
169
|
-
"""Build the import createstubs[_??] command to run on the board"""
|
170
|
-
cmd = ["mount", str(dest)] if dest else []
|
171
|
-
if variant == Variant.db:
|
172
|
-
cmd += ["exec", "import createstubs_db"]
|
173
|
-
elif variant == Variant.mem:
|
174
|
-
cmd += ["exec", "import createstubs_mem"]
|
175
|
-
else:
|
176
|
-
cmd += ["exec", "import createstubs"]
|
177
|
-
return cmd
|
178
|
-
|
179
|
-
|
180
|
-
def generate_board_stubs(
|
181
|
-
dest: Path,
|
182
|
-
mcu: MPRemoteBoard,
|
183
|
-
variant: Variant = Variant.db,
|
184
|
-
form: Form = Form.mpy,
|
185
|
-
host_mounted: bool = True,
|
186
|
-
) -> Tuple[int, Optional[Path]]:
|
187
|
-
"""
|
188
|
-
Generate the MCU stubs for this MCU board.
|
189
|
-
Parameters
|
190
|
-
----------
|
191
|
-
dest : Path
|
192
|
-
The destination folder for the stubs
|
193
|
-
port : str
|
194
|
-
The port the board is connected to
|
195
|
-
"""
|
196
|
-
|
197
|
-
# HOST -> MCU : copy createstubs to board
|
198
|
-
ok = copy_scripts_to_board(mcu, variant, form)
|
199
|
-
if not ok and not TESTING:
|
200
|
-
log.warning("Error copying createstubs to board")
|
201
|
-
return ERROR, None
|
202
|
-
|
203
|
-
copy_boardname_to_board(mcu)
|
204
|
-
|
205
|
-
rc, out = run_createstubs(dest, mcu, variant) # , host_mounted=host_mounted)
|
206
|
-
|
207
|
-
if rc != OK:
|
208
|
-
log.warning("Error running createstubs: %s", out)
|
209
|
-
return ERROR, None
|
210
|
-
|
211
|
-
if not host_mounted:
|
212
|
-
# Waiting for MPRemote to support copying folder from board to host
|
213
|
-
raise NotImplementedError("TODO: Copy stubs from board to host")
|
214
|
-
|
215
|
-
# Find the output starting with 'Path: '
|
216
|
-
folder = get_stubfolder(out)
|
217
|
-
if not folder:
|
218
|
-
return ERROR, None
|
219
|
-
|
220
|
-
stubs_path = dest / folder
|
221
|
-
mcu.path = stubs_path
|
222
|
-
# read the modules.json file into a dict
|
223
|
-
try:
|
224
|
-
with open(stubs_path / "modules.json") as fp:
|
225
|
-
modules_json = json.load(fp)
|
226
|
-
mcu.firmware = modules_json["firmware"]
|
227
|
-
except FileNotFoundError:
|
228
|
-
log.warning("Could not load modules.json, Assuming error in createstubs")
|
229
|
-
return ERROR, None
|
230
|
-
|
231
|
-
# check the number of stubs generated
|
232
|
-
if len(list(stubs_path.glob("*.p*"))) < 10:
|
233
|
-
log.warning("Error generating stubs, too few (<10)stubs were generated")
|
234
|
-
return ERROR, None
|
235
|
-
|
236
|
-
stubgen_needed = any(stubs_path.glob("*.py"))
|
237
|
-
utils.do_post_processing([stubs_path], stubgen=stubgen_needed, black=True, autoflake=True)
|
238
|
-
|
239
|
-
return OK, stubs_path
|
240
|
-
|
241
|
-
|
242
|
-
def copy_boardname_to_board(mcu: MPRemoteBoard):
|
243
|
-
"""
|
244
|
-
Copies the board name to the board by writing it to the 'boardname.py' file.
|
245
|
-
|
246
|
-
Args:
|
247
|
-
mcu: The MCU object representing the microcontroller.
|
248
|
-
|
249
|
-
Returns:
|
250
|
-
None
|
251
|
-
"""
|
252
|
-
if mcu.board:
|
253
|
-
cmd = ["exec", f"with open('lib/boardname.py', 'w') as f: f.write('BOARDNAME=\"{mcu.board}\"')"]
|
254
|
-
log.info(f"Writing BOARDNAME='{mcu.board}' to boardname.py")
|
255
|
-
else:
|
256
|
-
cmd = ["rm", "boardname.py"]
|
257
|
-
rc, _ = mcu.run_command(cmd)
|
258
|
-
if rc != OK and "rm" not in cmd:
|
259
|
-
log.error(f"Error during copy createstubs running command: {cmd}")
|
260
|
-
|
261
|
-
|
262
|
-
def copy_scripts_to_board(mcu: MPRemoteBoard, variant: Variant, form: Form):
|
263
|
-
"""
|
264
|
-
Copy scripts to the board.
|
265
|
-
|
266
|
-
Args:
|
267
|
-
mcu (str): The microcontroller unit.
|
268
|
-
variant (str): The variant of the board.
|
269
|
-
form (Form): The form of the scripts to be copied.
|
270
|
-
|
271
|
-
Returns:
|
272
|
-
bool: True if the scripts are successfully copied, False otherwise.
|
273
|
-
"""
|
274
|
-
if LOCAL_FILES:
|
275
|
-
return copy_createstubs_to_board(mcu, variant, form)
|
276
|
-
if form == Form.min:
|
277
|
-
location = "github:josverl/micropython-stubber/mip/minified.json"
|
278
|
-
elif form == Form.mpy:
|
279
|
-
location = "github:josverl/micropython-stubber/mip/mpy_v6.json"
|
280
|
-
else:
|
281
|
-
location = "github:josverl/micropython-stubber/mip/full.json"
|
282
|
-
|
283
|
-
return mcu.mip_install(location)
|
284
|
-
|
285
|
-
|
286
|
-
def get_stubfolder(out: List[str]):
|
287
|
-
return (
|
288
|
-
lines[-1].split("/remote/")[-1].strip() if (lines := [l for l in out if l.startswith("INFO : Path: ")]) else ""
|
289
|
-
)
|
290
|
-
|
291
|
-
|
292
|
-
def scan_boards(optimistic: bool = False) -> List[MPRemoteBoard]:
|
293
|
-
"""
|
294
|
-
This function scans for boards and returns a list of MPRemoteBoard objects.
|
295
|
-
:return: list of MPRemoteBoard objects
|
296
|
-
"""
|
297
|
-
boards = []
|
298
|
-
for mpr_port in MPRemoteBoard.connected_boards():
|
299
|
-
board = MPRemoteBoard(mpr_port)
|
300
|
-
log.debug(f"Attempt to connect to: {board.serialport}")
|
301
|
-
try:
|
302
|
-
board.get_mcu_info()
|
303
|
-
log.success(f"Detected board {board.description} {board.version}")
|
304
|
-
boards.append(board)
|
305
|
-
except Exception:
|
306
|
-
log.error(f"Failed to get mcu_info for {board.serialport}")
|
307
|
-
if optimistic:
|
308
|
-
boards.append(board)
|
309
|
-
continue
|
310
|
-
return boards
|
311
|
-
|
312
|
-
|
313
|
-
def set_loglevel(verbose: int) -> str:
|
314
|
-
"""Set log level based on verbose level
|
315
|
-
Get the level from the verbose setting (0=INFO, 1=DEBUG, 2=TRACE)
|
316
|
-
Set the format string, based on the level.
|
317
|
-
Add the handler to the logger, with the level and format string.
|
318
|
-
Return the level
|
319
|
-
"""
|
320
|
-
log.remove()
|
321
|
-
level = {0: "INFO", 1: "DEBUG", 2: "TRACE"}.get(verbose, "TRACE")
|
322
|
-
if level == "INFO":
|
323
|
-
format_str = "<green>{time:HH:mm:ss}</green>|<level>{level: <8}</level>|<cyan>{module: <20}</cyan> - <level>{message}</level>"
|
324
|
-
else:
|
325
|
-
format_str = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green>|<level>{level: <8}</level>|<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
|
326
|
-
|
327
|
-
log.add(sys.stderr, level=level, backtrace=True, diagnose=True, colorize=True, format=format_str)
|
328
|
-
# log.info(f"micropython-stubber {__version__}")
|
329
|
-
return level
|
330
|
-
|
331
|
-
|
332
|
-
def copy_to_repo(source: Path, fw: dict) -> Optional[Path]:
|
333
|
-
"""Copy the generated stubs to the stubs repo.
|
334
|
-
If the destination folder exists, it is first emptied
|
335
|
-
when successful: returns the destination path - None otherwise
|
336
|
-
"""
|
337
|
-
destination = CONFIG.stub_path / board_folder_name(fw)
|
338
|
-
try:
|
339
|
-
if destination.exists() and destination.is_dir():
|
340
|
-
# first clean the destination folder
|
341
|
-
shutil.rmtree(destination)
|
342
|
-
# copy all files and folder from the source to the destination
|
343
|
-
shutil.copytree(source, destination, dirs_exist_ok=True)
|
344
|
-
return destination
|
345
|
-
except OSError as e:
|
346
|
-
log.error(f"Error: {source} : {e.strerror}")
|
347
|
-
return None
|
348
|
-
|
349
|
-
|
350
|
-
def stub_connected_mcus(variant: str, format: str, debug: bool) -> int:
|
351
|
-
"""
|
352
|
-
Runs the stubber to generate stubs for connected MicroPython boards.
|
353
|
-
|
354
|
-
Args:
|
355
|
-
variant (str): The variant of the createstubs script.
|
356
|
-
format (str): The format of the createstubs script.
|
357
|
-
debug (bool): Flag indicating whether to enable debug mode.
|
358
|
-
|
359
|
-
Returns:
|
360
|
-
None
|
361
|
-
"""
|
362
|
-
|
363
|
-
if debug:
|
364
|
-
set_loglevel(1)
|
365
|
-
else:
|
366
|
-
set_loglevel(0)
|
367
|
-
variant = Variant(variant.lower())
|
368
|
-
form = Form(format.lower())
|
369
|
-
tempdir = mkdtemp(prefix="board_stubber")
|
370
|
-
temp_path = Path(tempdir)
|
371
|
-
|
372
|
-
all_built = []
|
373
|
-
|
374
|
-
# scan boards and just work with the ones that respond with understandable data
|
375
|
-
connected_boards = scan_boards(True)
|
376
|
-
if not connected_boards:
|
377
|
-
log.error("No micropython boards were found")
|
378
|
-
return ERROR
|
379
|
-
|
380
|
-
table = Table(show_header=True, header_style="bold magenta")
|
381
|
-
table.add_column("Serial Port")
|
382
|
-
table.add_column("Port")
|
383
|
-
table.add_column("Description")
|
384
|
-
table.add_column("Version")
|
385
|
-
|
386
|
-
for b in connected_boards:
|
387
|
-
table.add_row(b.serialport, b.port, b.description, b.version)
|
388
|
-
console = Console()
|
389
|
-
console.print(table)
|
390
|
-
|
391
|
-
# scan boards and generate stubs
|
392
|
-
for board in connected_boards:
|
393
|
-
log.info(
|
394
|
-
f"Connecting using {board.serialport} to {board.port} {board.board} {board.version}: {board.description}"
|
395
|
-
)
|
396
|
-
# remove the modulelist.done file before starting createstubs on each board
|
397
|
-
(temp_path / "modulelist.done").unlink(missing_ok=True)
|
398
|
-
|
399
|
-
rc, my_stubs = generate_board_stubs(temp_path, board, variant, form)
|
400
|
-
if rc != OK:
|
401
|
-
log.error(f"Failed to generate stubs for {board.serialport}")
|
402
|
-
continue
|
403
|
-
if my_stubs:
|
404
|
-
log.success(f'Stubs generated for {board.firmware["port"]}-{board.firmware["board"]}')
|
405
|
-
if destination := copy_to_repo(my_stubs, board.firmware):
|
406
|
-
log.success(f"Stubs copied to {destination}")
|
407
|
-
# Also merge the stubs with the docstubs
|
408
|
-
log.info(f"Merging stubs with docstubs : {board.firmware}")
|
409
|
-
|
410
|
-
merged = merge_all_docstubs(
|
411
|
-
versions=board.firmware["version"],
|
412
|
-
family=board.firmware["family"],
|
413
|
-
boards=board.firmware["board"],
|
414
|
-
ports=board.firmware["port"],
|
415
|
-
mpy_path=CONFIG.mpy_path,
|
416
|
-
)
|
417
|
-
if not merged:
|
418
|
-
log.error(f"Failed to merge stubs for {board.serialport}")
|
419
|
-
continue
|
420
|
-
# Then Build the package
|
421
|
-
log.info(f"Building package for {board.firmware}")
|
422
|
-
built = build_multiple(
|
423
|
-
versions=board.firmware["version"],
|
424
|
-
family=board.firmware["family"],
|
425
|
-
boards=board.firmware["board"],
|
426
|
-
ports=board.firmware["port"],
|
427
|
-
)
|
428
|
-
all_built.extend(built)
|
429
|
-
|
430
|
-
if all_built:
|
431
|
-
print_result_table(all_built, console)
|
432
|
-
log.success("Done")
|
433
|
-
return OK
|
434
|
-
log.error(f"Failed to generate stubs for {board.serialport}")
|
435
|
-
return ERROR
|
436
|
-
|
437
|
-
|
438
|
-
def print_result_table(all_built: List, console: Console):
|
439
|
-
# create a rich table of the results and print it'
|
440
|
-
table = Table(title="Results")
|
441
|
-
|
442
|
-
table.add_column("Result", style="cyan")
|
443
|
-
table.add_column("Name/Path", style="cyan")
|
444
|
-
table.add_column("Version", style="green")
|
445
|
-
table.add_column("Error", style="red")
|
446
|
-
|
447
|
-
for result in all_built:
|
448
|
-
table.add_row(
|
449
|
-
result["result"],
|
450
|
-
(result["name"] + "\n" + result["path"]).strip(),
|
451
|
-
result["version"],
|
452
|
-
result["error"],
|
453
|
-
)
|
454
|
-
console.print(table)
|
1
|
+
"""
|
2
|
+
This script creates stubs on and for a connected micropython MCU board.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import json
|
6
|
+
import shutil
|
7
|
+
import sys
|
8
|
+
import time
|
9
|
+
from enum import Enum
|
10
|
+
from pathlib import Path
|
11
|
+
from tempfile import mkdtemp
|
12
|
+
from typing import List, Optional, Tuple
|
13
|
+
|
14
|
+
from loguru import logger as log
|
15
|
+
from rich.console import Console
|
16
|
+
from rich.table import Table
|
17
|
+
from tenacity import retry, stop_after_attempt, wait_fixed
|
18
|
+
|
19
|
+
from mpflash.mpremoteboard import ERROR, OK, MPRemoteBoard
|
20
|
+
from stubber import utils
|
21
|
+
from stubber.publish.merge_docstubs import merge_all_docstubs
|
22
|
+
from stubber.publish.pathnames import board_folder_name
|
23
|
+
from stubber.publish.publish import build_multiple
|
24
|
+
from stubber.utils.config import CONFIG
|
25
|
+
|
26
|
+
HERE = Path(__file__).parent
|
27
|
+
###############################################################################################
|
28
|
+
# TODO: promote to cmdline params
|
29
|
+
LOCAL_FILES = False
|
30
|
+
reset_before = True
|
31
|
+
TESTING = False
|
32
|
+
###############################################################################################
|
33
|
+
|
34
|
+
|
35
|
+
###############################################################################################
|
36
|
+
|
37
|
+
|
38
|
+
class Variant(str, Enum):
|
39
|
+
"""Variants to generate stubs on a MCU"""
|
40
|
+
|
41
|
+
full = "full"
|
42
|
+
mem = "mem"
|
43
|
+
db = "db"
|
44
|
+
|
45
|
+
|
46
|
+
class Form(str, Enum):
|
47
|
+
"""Optimization forms of scripts"""
|
48
|
+
|
49
|
+
py = "py"
|
50
|
+
min = "min"
|
51
|
+
mpy = "mpy"
|
52
|
+
|
53
|
+
|
54
|
+
def copy_createstubs_to_board(board: MPRemoteBoard, variant: Variant, form: Form) -> bool:
|
55
|
+
# sourcery skip: assign-if-exp, boolean-if-exp-identity, remove-unnecessary-cast
|
56
|
+
"""Copy createstubs to the board"""
|
57
|
+
# copy createstubs.py to the destination folder
|
58
|
+
origin = "./src/stubber/board"
|
59
|
+
|
60
|
+
_py = [
|
61
|
+
"rm :lib/createstubs.mpy",
|
62
|
+
"rm :lib/createstubs_mem.mpy",
|
63
|
+
"rm :lib/createstubs_db.mpy",
|
64
|
+
"rm :lib/createstubs.py",
|
65
|
+
"rm :lib/createstubs_mem.py",
|
66
|
+
"rm :lib/createstubs_db.py",
|
67
|
+
f"cp {origin}/createstubs.py :lib/createstubs.py",
|
68
|
+
f"cp {origin}/createstubs_mem.py :lib/createstubs_mem.py",
|
69
|
+
f"cp {origin}/createstubs_db.py :lib/createstubs_db.py",
|
70
|
+
]
|
71
|
+
|
72
|
+
# copy createstubs*_min.py to the destination folder
|
73
|
+
_min = [
|
74
|
+
f"cp {origin}/createstubs_min.py :lib/createstubs.py",
|
75
|
+
f"cp {origin}/createstubs_mem_min.py :lib/createstubs_mem.py",
|
76
|
+
f"cp {origin}/createstubs_db_min.py :lib/createstubs_db.py",
|
77
|
+
]
|
78
|
+
# copy createstubs*_mpy.mpy to the destination folder
|
79
|
+
_mpy = [
|
80
|
+
"rm :lib/createstubs.py",
|
81
|
+
"rm :lib/createstubs_mem.py",
|
82
|
+
"rm :lib/createstubs_db.py",
|
83
|
+
f"cp {origin}/createstubs_mpy.mpy :lib/createstubs.mpy",
|
84
|
+
f"cp {origin}/createstubs_mem_mpy.mpy :lib/createstubs_mem.mpy",
|
85
|
+
f"cp {origin}/createstubs_db_mpy.mpy :lib/createstubs_db.mpy",
|
86
|
+
]
|
87
|
+
|
88
|
+
_lib = [
|
89
|
+
[
|
90
|
+
"exec",
|
91
|
+
"import os;os.mkdir('lib') if not ('lib' in os.listdir()) else print('folder lib already exists')",
|
92
|
+
]
|
93
|
+
]
|
94
|
+
|
95
|
+
_get_ready = [
|
96
|
+
"rm :modulelist.done",
|
97
|
+
"rm :no_auto_stubber.txt",
|
98
|
+
f"cp {origin}/modulelist.txt :lib/modulelist.txt",
|
99
|
+
]
|
100
|
+
if form == Form.py:
|
101
|
+
do = _lib + _py + _get_ready
|
102
|
+
elif form == Form.min:
|
103
|
+
do = _lib + _min + _get_ready
|
104
|
+
else:
|
105
|
+
do = _lib + _mpy + _get_ready
|
106
|
+
|
107
|
+
# assume all ok, unless one is not ok
|
108
|
+
for cmd in do:
|
109
|
+
if isinstance(cmd, str) and cmd.startswith("rm "):
|
110
|
+
log_errors = False
|
111
|
+
else:
|
112
|
+
log_errors = True
|
113
|
+
rc, _ = board.run_command(cmd, log_errors=log_errors)
|
114
|
+
if rc != OK and "rm" not in cmd:
|
115
|
+
log.error(f"Error during copy createstubs running command: {cmd}")
|
116
|
+
return False
|
117
|
+
return True
|
118
|
+
|
119
|
+
|
120
|
+
@retry(stop=stop_after_attempt(4), wait=wait_fixed(2))
|
121
|
+
def hard_reset(board: MPRemoteBoard) -> bool:
|
122
|
+
"""Reset the board"""
|
123
|
+
# do not run "exec", "import machine;machine.reset()" as it will hang an esp32
|
124
|
+
rc, _ = board.run_command(["reset"], timeout=5)
|
125
|
+
board.connected = False
|
126
|
+
return rc == OK
|
127
|
+
|
128
|
+
|
129
|
+
@retry(stop=stop_after_attempt(10), wait=wait_fixed(15))
|
130
|
+
def run_createstubs(dest: Path, mcu: MPRemoteBoard, variant: Variant = Variant.db):
|
131
|
+
"""
|
132
|
+
Run a createstubs[variant] on the provided board.
|
133
|
+
Retry running the command up to 10 times, with a 15 second timeout between retries.
|
134
|
+
this should allow for the boards with little memory to complete even if they run out of memory.
|
135
|
+
"""
|
136
|
+
# add the lib folder to the path
|
137
|
+
cmd_path = [
|
138
|
+
"exec",
|
139
|
+
'import sys;sys.path.append("/lib") if "/lib" not in sys.path else "/lib already in path"',
|
140
|
+
]
|
141
|
+
mcu.run_command(cmd_path, timeout=5)
|
142
|
+
|
143
|
+
if reset_before:
|
144
|
+
log.info(f"Resetting {mcu.serialport} {mcu.description}")
|
145
|
+
mcu.run_command("reset", timeout=5)
|
146
|
+
time.sleep(2)
|
147
|
+
|
148
|
+
log.info(f"Running createstubs {variant.value} on {mcu.serialport} {mcu.description} using temp path: {dest}")
|
149
|
+
cmd = build_cmd(dest, variant)
|
150
|
+
log.info(f"Running : mpremote {' '.join(cmd)}")
|
151
|
+
mcu.run_command.retry.wait = wait_fixed(15)
|
152
|
+
# some boards need 2-3 minutes to run createstubs - so increase the default timeout
|
153
|
+
# esp32s3 > 240 seconds with mounted fs
|
154
|
+
# but slows down esp8266 restarts so keep that to 90 seconds
|
155
|
+
timeout = 90 if mcu.port == "esp8266" else 6 * 60 # type: ignore
|
156
|
+
rc, out = mcu.run_command(cmd, timeout=timeout)
|
157
|
+
# check last line for exception or error and raise that if found
|
158
|
+
if rc != OK and out and ":" in out[-1] and not out[-1].startswith("INFO") and not out[-1].startswith("WARN"):
|
159
|
+
log.warning(f"createstubs: {out[-1]}")
|
160
|
+
raise RuntimeError(out[-1]) from eval(out[-1].split(":")[0])
|
161
|
+
|
162
|
+
if rc != OK and variant == Variant.db:
|
163
|
+
# assume createstubs ran out of memory and try again
|
164
|
+
raise MemoryError("Memory error, try again")
|
165
|
+
return rc, out
|
166
|
+
|
167
|
+
|
168
|
+
def build_cmd(dest: Path, variant: Variant = Variant.db):
|
169
|
+
"""Build the import createstubs[_??] command to run on the board"""
|
170
|
+
cmd = ["mount", str(dest)] if dest else []
|
171
|
+
if variant == Variant.db:
|
172
|
+
cmd += ["exec", "import createstubs_db"]
|
173
|
+
elif variant == Variant.mem:
|
174
|
+
cmd += ["exec", "import createstubs_mem"]
|
175
|
+
else:
|
176
|
+
cmd += ["exec", "import createstubs"]
|
177
|
+
return cmd
|
178
|
+
|
179
|
+
|
180
|
+
def generate_board_stubs(
|
181
|
+
dest: Path,
|
182
|
+
mcu: MPRemoteBoard,
|
183
|
+
variant: Variant = Variant.db,
|
184
|
+
form: Form = Form.mpy,
|
185
|
+
host_mounted: bool = True,
|
186
|
+
) -> Tuple[int, Optional[Path]]:
|
187
|
+
"""
|
188
|
+
Generate the MCU stubs for this MCU board.
|
189
|
+
Parameters
|
190
|
+
----------
|
191
|
+
dest : Path
|
192
|
+
The destination folder for the stubs
|
193
|
+
port : str
|
194
|
+
The port the board is connected to
|
195
|
+
"""
|
196
|
+
|
197
|
+
# HOST -> MCU : copy createstubs to board
|
198
|
+
ok = copy_scripts_to_board(mcu, variant, form)
|
199
|
+
if not ok and not TESTING:
|
200
|
+
log.warning("Error copying createstubs to board")
|
201
|
+
return ERROR, None
|
202
|
+
|
203
|
+
copy_boardname_to_board(mcu)
|
204
|
+
|
205
|
+
rc, out = run_createstubs(dest, mcu, variant) # , host_mounted=host_mounted)
|
206
|
+
|
207
|
+
if rc != OK:
|
208
|
+
log.warning("Error running createstubs: %s", out)
|
209
|
+
return ERROR, None
|
210
|
+
|
211
|
+
if not host_mounted:
|
212
|
+
# Waiting for MPRemote to support copying folder from board to host
|
213
|
+
raise NotImplementedError("TODO: Copy stubs from board to host")
|
214
|
+
|
215
|
+
# Find the output starting with 'Path: '
|
216
|
+
folder = get_stubfolder(out)
|
217
|
+
if not folder:
|
218
|
+
return ERROR, None
|
219
|
+
|
220
|
+
stubs_path = dest / folder
|
221
|
+
mcu.path = stubs_path
|
222
|
+
# read the modules.json file into a dict
|
223
|
+
try:
|
224
|
+
with open(stubs_path / "modules.json") as fp:
|
225
|
+
modules_json = json.load(fp)
|
226
|
+
mcu.firmware = modules_json["firmware"]
|
227
|
+
except FileNotFoundError:
|
228
|
+
log.warning("Could not load modules.json, Assuming error in createstubs")
|
229
|
+
return ERROR, None
|
230
|
+
|
231
|
+
# check the number of stubs generated
|
232
|
+
if len(list(stubs_path.glob("*.p*"))) < 10:
|
233
|
+
log.warning("Error generating stubs, too few (<10)stubs were generated")
|
234
|
+
return ERROR, None
|
235
|
+
|
236
|
+
stubgen_needed = any(stubs_path.glob("*.py"))
|
237
|
+
utils.do_post_processing([stubs_path], stubgen=stubgen_needed, black=True, autoflake=True)
|
238
|
+
|
239
|
+
return OK, stubs_path
|
240
|
+
|
241
|
+
|
242
|
+
def copy_boardname_to_board(mcu: MPRemoteBoard):
|
243
|
+
"""
|
244
|
+
Copies the board name to the board by writing it to the 'boardname.py' file.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
mcu: The MCU object representing the microcontroller.
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
None
|
251
|
+
"""
|
252
|
+
if mcu.board:
|
253
|
+
cmd = ["exec", f"with open('lib/boardname.py', 'w') as f: f.write('BOARDNAME=\"{mcu.board}\"')"]
|
254
|
+
log.info(f"Writing BOARDNAME='{mcu.board}' to boardname.py")
|
255
|
+
else:
|
256
|
+
cmd = ["rm", "boardname.py"]
|
257
|
+
rc, _ = mcu.run_command(cmd)
|
258
|
+
if rc != OK and "rm" not in cmd:
|
259
|
+
log.error(f"Error during copy createstubs running command: {cmd}")
|
260
|
+
|
261
|
+
|
262
|
+
def copy_scripts_to_board(mcu: MPRemoteBoard, variant: Variant, form: Form):
|
263
|
+
"""
|
264
|
+
Copy scripts to the board.
|
265
|
+
|
266
|
+
Args:
|
267
|
+
mcu (str): The microcontroller unit.
|
268
|
+
variant (str): The variant of the board.
|
269
|
+
form (Form): The form of the scripts to be copied.
|
270
|
+
|
271
|
+
Returns:
|
272
|
+
bool: True if the scripts are successfully copied, False otherwise.
|
273
|
+
"""
|
274
|
+
if LOCAL_FILES:
|
275
|
+
return copy_createstubs_to_board(mcu, variant, form)
|
276
|
+
if form == Form.min:
|
277
|
+
location = "github:josverl/micropython-stubber/mip/minified.json"
|
278
|
+
elif form == Form.mpy:
|
279
|
+
location = "github:josverl/micropython-stubber/mip/mpy_v6.json"
|
280
|
+
else:
|
281
|
+
location = "github:josverl/micropython-stubber/mip/full.json"
|
282
|
+
|
283
|
+
return mcu.mip_install(location)
|
284
|
+
|
285
|
+
|
286
|
+
def get_stubfolder(out: List[str]):
|
287
|
+
return (
|
288
|
+
lines[-1].split("/remote/")[-1].strip() if (lines := [l for l in out if l.startswith("INFO : Path: ")]) else ""
|
289
|
+
)
|
290
|
+
|
291
|
+
|
292
|
+
def scan_boards(optimistic: bool = False) -> List[MPRemoteBoard]:
|
293
|
+
"""
|
294
|
+
This function scans for boards and returns a list of MPRemoteBoard objects.
|
295
|
+
:return: list of MPRemoteBoard objects
|
296
|
+
"""
|
297
|
+
boards = []
|
298
|
+
for mpr_port in MPRemoteBoard.connected_boards():
|
299
|
+
board = MPRemoteBoard(mpr_port)
|
300
|
+
log.debug(f"Attempt to connect to: {board.serialport}")
|
301
|
+
try:
|
302
|
+
board.get_mcu_info()
|
303
|
+
log.success(f"Detected board {board.description} {board.version}")
|
304
|
+
boards.append(board)
|
305
|
+
except Exception:
|
306
|
+
log.error(f"Failed to get mcu_info for {board.serialport}")
|
307
|
+
if optimistic:
|
308
|
+
boards.append(board)
|
309
|
+
continue
|
310
|
+
return boards
|
311
|
+
|
312
|
+
|
313
|
+
def set_loglevel(verbose: int) -> str:
|
314
|
+
"""Set log level based on verbose level
|
315
|
+
Get the level from the verbose setting (0=INFO, 1=DEBUG, 2=TRACE)
|
316
|
+
Set the format string, based on the level.
|
317
|
+
Add the handler to the logger, with the level and format string.
|
318
|
+
Return the level
|
319
|
+
"""
|
320
|
+
log.remove()
|
321
|
+
level = {0: "INFO", 1: "DEBUG", 2: "TRACE"}.get(verbose, "TRACE")
|
322
|
+
if level == "INFO":
|
323
|
+
format_str = "<green>{time:HH:mm:ss}</green>|<level>{level: <8}</level>|<cyan>{module: <20}</cyan> - <level>{message}</level>"
|
324
|
+
else:
|
325
|
+
format_str = "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green>|<level>{level: <8}</level>|<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
|
326
|
+
|
327
|
+
log.add(sys.stderr, level=level, backtrace=True, diagnose=True, colorize=True, format=format_str)
|
328
|
+
# log.info(f"micropython-stubber {__version__}")
|
329
|
+
return level
|
330
|
+
|
331
|
+
|
332
|
+
def copy_to_repo(source: Path, fw: dict) -> Optional[Path]:
|
333
|
+
"""Copy the generated stubs to the stubs repo.
|
334
|
+
If the destination folder exists, it is first emptied
|
335
|
+
when successful: returns the destination path - None otherwise
|
336
|
+
"""
|
337
|
+
destination = CONFIG.stub_path / board_folder_name(fw)
|
338
|
+
try:
|
339
|
+
if destination.exists() and destination.is_dir():
|
340
|
+
# first clean the destination folder
|
341
|
+
shutil.rmtree(destination)
|
342
|
+
# copy all files and folder from the source to the destination
|
343
|
+
shutil.copytree(source, destination, dirs_exist_ok=True)
|
344
|
+
return destination
|
345
|
+
except OSError as e:
|
346
|
+
log.error(f"Error: {source} : {e.strerror}")
|
347
|
+
return None
|
348
|
+
|
349
|
+
|
350
|
+
def stub_connected_mcus(variant: str, format: str, debug: bool) -> int:
|
351
|
+
"""
|
352
|
+
Runs the stubber to generate stubs for connected MicroPython boards.
|
353
|
+
|
354
|
+
Args:
|
355
|
+
variant (str): The variant of the createstubs script.
|
356
|
+
format (str): The format of the createstubs script.
|
357
|
+
debug (bool): Flag indicating whether to enable debug mode.
|
358
|
+
|
359
|
+
Returns:
|
360
|
+
None
|
361
|
+
"""
|
362
|
+
|
363
|
+
if debug:
|
364
|
+
set_loglevel(1)
|
365
|
+
else:
|
366
|
+
set_loglevel(0)
|
367
|
+
variant = Variant(variant.lower())
|
368
|
+
form = Form(format.lower())
|
369
|
+
tempdir = mkdtemp(prefix="board_stubber")
|
370
|
+
temp_path = Path(tempdir)
|
371
|
+
|
372
|
+
all_built = []
|
373
|
+
|
374
|
+
# scan boards and just work with the ones that respond with understandable data
|
375
|
+
connected_boards = scan_boards(True)
|
376
|
+
if not connected_boards:
|
377
|
+
log.error("No micropython boards were found")
|
378
|
+
return ERROR
|
379
|
+
|
380
|
+
table = Table(show_header=True, header_style="bold magenta")
|
381
|
+
table.add_column("Serial Port")
|
382
|
+
table.add_column("Port")
|
383
|
+
table.add_column("Description")
|
384
|
+
table.add_column("Version")
|
385
|
+
|
386
|
+
for b in connected_boards:
|
387
|
+
table.add_row(b.serialport, b.port, b.description, b.version)
|
388
|
+
console = Console()
|
389
|
+
console.print(table)
|
390
|
+
|
391
|
+
# scan boards and generate stubs
|
392
|
+
for board in connected_boards:
|
393
|
+
log.info(
|
394
|
+
f"Connecting using {board.serialport} to {board.port} {board.board} {board.version}: {board.description}"
|
395
|
+
)
|
396
|
+
# remove the modulelist.done file before starting createstubs on each board
|
397
|
+
(temp_path / "modulelist.done").unlink(missing_ok=True)
|
398
|
+
|
399
|
+
rc, my_stubs = generate_board_stubs(temp_path, board, variant, form)
|
400
|
+
if rc != OK:
|
401
|
+
log.error(f"Failed to generate stubs for {board.serialport}")
|
402
|
+
continue
|
403
|
+
if my_stubs:
|
404
|
+
log.success(f'Stubs generated for {board.firmware["port"]}-{board.firmware["board"]}')
|
405
|
+
if destination := copy_to_repo(my_stubs, board.firmware):
|
406
|
+
log.success(f"Stubs copied to {destination}")
|
407
|
+
# Also merge the stubs with the docstubs
|
408
|
+
log.info(f"Merging stubs with docstubs : {board.firmware}")
|
409
|
+
|
410
|
+
merged = merge_all_docstubs(
|
411
|
+
versions=board.firmware["version"],
|
412
|
+
family=board.firmware["family"],
|
413
|
+
boards=board.firmware["board"],
|
414
|
+
ports=board.firmware["port"],
|
415
|
+
mpy_path=CONFIG.mpy_path,
|
416
|
+
)
|
417
|
+
if not merged:
|
418
|
+
log.error(f"Failed to merge stubs for {board.serialport}")
|
419
|
+
continue
|
420
|
+
# Then Build the package
|
421
|
+
log.info(f"Building package for {board.firmware}")
|
422
|
+
built = build_multiple(
|
423
|
+
versions=board.firmware["version"],
|
424
|
+
family=board.firmware["family"],
|
425
|
+
boards=board.firmware["board"],
|
426
|
+
ports=board.firmware["port"],
|
427
|
+
)
|
428
|
+
all_built.extend(built)
|
429
|
+
|
430
|
+
if all_built:
|
431
|
+
print_result_table(all_built, console)
|
432
|
+
log.success("Done")
|
433
|
+
return OK
|
434
|
+
log.error(f"Failed to generate stubs for {board.serialport}")
|
435
|
+
return ERROR
|
436
|
+
|
437
|
+
|
438
|
+
def print_result_table(all_built: List, console: Console):
|
439
|
+
# create a rich table of the results and print it'
|
440
|
+
table = Table(title="Results")
|
441
|
+
|
442
|
+
table.add_column("Result", style="cyan")
|
443
|
+
table.add_column("Name/Path", style="cyan")
|
444
|
+
table.add_column("Version", style="green")
|
445
|
+
table.add_column("Error", style="red")
|
446
|
+
|
447
|
+
for result in all_built:
|
448
|
+
table.add_row(
|
449
|
+
result["result"],
|
450
|
+
(result["name"] + "\n" + result["path"]).strip(),
|
451
|
+
result["version"],
|
452
|
+
result["error"],
|
453
|
+
)
|
454
|
+
console.print(table)
|