micropython-stubber 1.23.2__py3-none-any.whl → 1.24.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.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/METADATA +30 -12
- {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/RECORD +69 -66
- {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/WHEEL +1 -1
- mpflash/README.md +2 -2
- mpflash/mpflash/basicgit.py +49 -9
- mpflash/mpflash/common.py +23 -16
- mpflash/mpflash/downloaded.py +10 -2
- mpflash/mpflash/mpboard_id/__init__.py +9 -4
- mpflash/mpflash/mpboard_id/add_boards.py +25 -14
- mpflash/mpflash/mpboard_id/board.py +2 -2
- mpflash/mpflash/mpboard_id/board_id.py +10 -6
- mpflash/mpflash/mpboard_id/board_info.zip +0 -0
- mpflash/mpflash/mpboard_id/store.py +8 -3
- mpflash/mpflash/mpremoteboard/__init__.py +13 -8
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +27 -16
- mpflash/mpflash/vendor/board_database.py +185 -0
- mpflash/mpflash/vendor/readme.md +10 -1
- mpflash/mpflash/versions.py +28 -40
- mpflash/poetry.lock +1605 -601
- mpflash/pyproject.toml +4 -3
- stubber/__init__.py +1 -1
- stubber/board/createstubs.py +51 -27
- stubber/board/createstubs_db.py +36 -28
- stubber/board/createstubs_db_min.py +171 -165
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +36 -28
- stubber/board/createstubs_mem_min.py +184 -178
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +102 -94
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/modulelist.txt +16 -0
- stubber/codemod/enrich.py +297 -88
- stubber/codemod/merge_docstub.py +250 -65
- stubber/codemod/test_enrich.py +87 -0
- stubber/codemod/visitors/typevars.py +200 -0
- stubber/commands/build_cmd.py +16 -3
- stubber/commands/clone_cmd.py +3 -3
- stubber/commands/config_cmd.py +4 -2
- stubber/commands/enrich_folder_cmd.py +33 -21
- stubber/commands/get_core_cmd.py +1 -2
- stubber/commands/get_docstubs_cmd.py +60 -6
- stubber/commands/get_frozen_cmd.py +15 -12
- stubber/commands/get_mcu_cmd.py +3 -3
- stubber/commands/merge_cmd.py +1 -2
- stubber/commands/publish_cmd.py +19 -4
- stubber/commands/stub_cmd.py +3 -3
- stubber/commands/switch_cmd.py +3 -5
- stubber/commands/variants_cmd.py +3 -3
- stubber/cst_transformer.py +52 -17
- stubber/freeze/common.py +27 -11
- stubber/freeze/freeze_manifest_2.py +8 -1
- stubber/freeze/get_frozen.py +4 -1
- stubber/merge_config.py +111 -0
- stubber/minify.py +1 -2
- stubber/publish/database.py +51 -10
- stubber/publish/merge_docstubs.py +33 -16
- stubber/publish/package.py +32 -18
- stubber/publish/publish.py +8 -8
- stubber/publish/stubpackage.py +110 -47
- stubber/rst/lookup.py +205 -43
- stubber/rst/reader.py +106 -59
- stubber/rst/rst_utils.py +24 -11
- stubber/stubber.py +1 -1
- stubber/stubs_from_docs.py +31 -13
- stubber/update_module_list.py +2 -2
- stubber/utils/config.py +33 -13
- stubber/utils/post.py +9 -6
- stubber/publish/missing_class_methods.py +0 -51
- {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/entry_points.txt +0 -0
stubber/commands/variants_cmd.py
CHANGED
@@ -4,11 +4,11 @@ from pathlib import Path
|
|
4
4
|
|
5
5
|
import rich_click as click
|
6
6
|
from mpflash.logger import log
|
7
|
+
|
8
|
+
import stubber
|
9
|
+
from stubber.commands.cli import stubber_cli
|
7
10
|
from stubber.utils.config import CONFIG
|
8
11
|
from stubber.variants import create_variants
|
9
|
-
import stubber
|
10
|
-
|
11
|
-
from .cli import stubber_cli
|
12
12
|
|
13
13
|
|
14
14
|
@click.option(
|
stubber/cst_transformer.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""helper functions for stub transformations"""
|
2
2
|
|
3
3
|
# sourcery skip: snake-case-functions
|
4
|
-
from dataclasses import dataclass
|
4
|
+
from dataclasses import dataclass, field
|
5
5
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
6
6
|
|
7
7
|
import libcst as cst
|
@@ -9,14 +9,27 @@ import libcst as cst
|
|
9
9
|
|
10
10
|
@dataclass
|
11
11
|
class TypeInfo:
|
12
|
-
"contains the
|
12
|
+
"contains the functionDefs and classDefs info read from the stubs source"
|
13
13
|
name: str
|
14
14
|
decorators: Sequence[cst.Decorator]
|
15
15
|
params: Optional[cst.Parameters] = None
|
16
16
|
returns: Optional[cst.Annotation] = None
|
17
17
|
docstr_node: Optional[cst.SimpleStatementLine] = None
|
18
18
|
def_node: Optional[Union[cst.FunctionDef, cst.ClassDef]] = None
|
19
|
-
def_type: str = "?" #
|
19
|
+
def_type: str = "?" # funcDef or classDef or module
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class AnnoValue:
|
24
|
+
"The different values for the annotations"
|
25
|
+
docstring: Optional[str] = "" # strings
|
26
|
+
"Module docstring or function/method docstring"
|
27
|
+
docstring_node: Optional[cst.SimpleStatementLine] = None
|
28
|
+
"the docstring node for a function method to reuse with overloads"
|
29
|
+
type_info: Optional[TypeInfo] = None # simple type
|
30
|
+
"function/method or class definition read from the docstub source"
|
31
|
+
overloads: List[TypeInfo] = field(default_factory=list)
|
32
|
+
"function / method overloads read from the docstub source"
|
20
33
|
|
21
34
|
|
22
35
|
class TransformError(Exception):
|
@@ -27,7 +40,7 @@ class TransformError(Exception):
|
|
27
40
|
|
28
41
|
|
29
42
|
MODULE_KEY = ("__module",)
|
30
|
-
|
43
|
+
MOD_DOCSTR_KEY = ("__module_docstring",)
|
31
44
|
|
32
45
|
# debug helper
|
33
46
|
_m = cst.parse_module("")
|
@@ -44,17 +57,18 @@ class StubTypingCollector(cst.CSTVisitor):
|
|
44
57
|
# store the annotations
|
45
58
|
self.annotations: Dict[
|
46
59
|
Tuple[str, ...], # key: tuple of canonical class/function name
|
47
|
-
|
60
|
+
AnnoValue, # The TypeInfo or list of TypeInfo
|
48
61
|
] = {}
|
49
|
-
self.comments
|
62
|
+
self.comments: List[str] = []
|
50
63
|
|
51
64
|
# ------------------------------------------------------------
|
52
65
|
def visit_Module(self, node: cst.Module) -> bool:
|
53
66
|
"""Store the module docstring"""
|
54
67
|
docstr = node.get_docstring()
|
55
68
|
if docstr:
|
56
|
-
self.annotations[MODULE_KEY] = docstr
|
69
|
+
self.annotations[MODULE_KEY] = AnnoValue(docstring=docstr)
|
57
70
|
return True
|
71
|
+
|
58
72
|
def visit_Comment(self, node: cst.Comment) -> None:
|
59
73
|
"""
|
60
74
|
connect comments from the source
|
@@ -82,7 +96,7 @@ class StubTypingCollector(cst.CSTVisitor):
|
|
82
96
|
def_type="classdef",
|
83
97
|
def_node=node,
|
84
98
|
)
|
85
|
-
self.annotations[tuple(self.stack)] = ti
|
99
|
+
self.annotations[tuple(self.stack)] = AnnoValue(type_info=ti)
|
86
100
|
|
87
101
|
def leave_ClassDef(self, original_node: cst.ClassDef) -> None:
|
88
102
|
"""remove the class name from the stack"""
|
@@ -105,7 +119,14 @@ class StubTypingCollector(cst.CSTVisitor):
|
|
105
119
|
def_type="funcdef",
|
106
120
|
def_node=node,
|
107
121
|
)
|
108
|
-
|
122
|
+
key = tuple(self.stack)
|
123
|
+
if not key in self.annotations:
|
124
|
+
# store the first function/method signature
|
125
|
+
self.annotations[key] = AnnoValue(type_info=ti)
|
126
|
+
|
127
|
+
if any(dec.decorator.value == "overload" for dec in node.decorators): # type: ignore
|
128
|
+
# and store the overloads
|
129
|
+
self.annotations[key].overloads.append(ti)
|
109
130
|
|
110
131
|
def update_append_first_node(self, node):
|
111
132
|
"""Store the function/method docstring or function/method sig"""
|
@@ -123,17 +144,31 @@ class StubTypingCollector(cst.CSTVisitor):
|
|
123
144
|
|
124
145
|
def update_def_docstr(
|
125
146
|
dest_node: Union[cst.FunctionDef, cst.ClassDef],
|
126
|
-
|
147
|
+
src_docstr: Optional[Union[cst.SimpleStatementLine, str]] = None,
|
127
148
|
src_node=None,
|
128
149
|
) -> Any:
|
129
150
|
"""
|
130
151
|
Update the docstring of a function/method or class
|
152
|
+
The supplied `src_docstr` can be a string or a SimpleStatementLine
|
131
153
|
|
132
|
-
for
|
133
|
-
in this case
|
154
|
+
for function defs ending in an ellipsis, the entire body needs to be replaced.
|
155
|
+
in this case `src_node` is required.
|
134
156
|
"""
|
135
|
-
if not
|
157
|
+
if not src_docstr:
|
136
158
|
return dest_node
|
159
|
+
if isinstance(src_docstr, str):
|
160
|
+
if not src_docstr[0] in ('"', "'"):
|
161
|
+
src_docstr = f'"""{src_docstr}"""'
|
162
|
+
# convert the string to a SimpleStatementLine
|
163
|
+
src_docstr = cst.SimpleStatementLine(
|
164
|
+
body=[
|
165
|
+
cst.Expr(
|
166
|
+
value=cst.SimpleString(
|
167
|
+
value=src_docstr,
|
168
|
+
),
|
169
|
+
),
|
170
|
+
]
|
171
|
+
)
|
137
172
|
|
138
173
|
# function def on a single line ending with an ellipsis (...)
|
139
174
|
if isinstance(dest_node.body, cst.SimpleStatementSuite):
|
@@ -144,13 +179,13 @@ def update_def_docstr(
|
|
144
179
|
raise TransformError("Expected Def with Indented body")
|
145
180
|
|
146
181
|
# classdef of functiondef with an indented body
|
147
|
-
# need some
|
148
|
-
# note : indented body is nested : body.body
|
182
|
+
# need some funky casting to avoid issues with changing the body
|
183
|
+
# note : indented body is nested : IndentedBlock.body.body
|
149
184
|
if dest_node.get_docstring() is None:
|
150
185
|
# append the new docstring and append the function body
|
151
|
-
body = tuple([
|
186
|
+
body = tuple([src_docstr] + list(dest_node.body.body))
|
152
187
|
else:
|
153
|
-
body = tuple([
|
188
|
+
body = tuple([src_docstr] + list(dest_node.body.body[1:]))
|
154
189
|
body_2 = dest_node.body.with_changes(body=body)
|
155
190
|
|
156
191
|
return dest_node.with_changes(body=body_2)
|
stubber/freeze/common.py
CHANGED
@@ -59,14 +59,30 @@ def apply_frozen_module_fixes(freeze_path: Path, mpy_path: Path):
|
|
59
59
|
with open(freeze_path / "umqtt" / "__init__.py", "a") as f:
|
60
60
|
f.write("")
|
61
61
|
|
62
|
-
# NOTE: FIX 2 compensate for expicitly omited task.py from freeze manifest
|
63
|
-
# this is normally implemented as a C module, let's use the .py version to generate a stub for this
|
64
|
-
if (freeze_path / "
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
62
|
+
# # NOTE: FIX 2 compensate for expicitly omited task.py from freeze manifest
|
63
|
+
# # this is normally implemented as a C module, let's use the .py version to generate a stub for this
|
64
|
+
# if (freeze_path / "asyncio").exists() and not (freeze_path / "asyncio" / "task.py").exists():
|
65
|
+
# # copy task.py from micropython\extmod\asyncio\task.py to stub_folder
|
66
|
+
# log.debug("add missing : asyncio/task.py")
|
67
|
+
# task_py = mpy_path / "extmod" / "asyncio" / "task.py"
|
68
|
+
# try:
|
69
|
+
# shutil.copy(str(task_py), str(freeze_path / "asyncio"))
|
70
|
+
# except OSError as er:
|
71
|
+
# log.warning(f"error copying {task_py} : {er}")
|
72
|
+
# # try to continue
|
73
|
+
|
74
|
+
RM_FREEZE = ["asyncio", "uasyncio.py", "rp2.py"]
|
75
|
+
removed = []
|
76
|
+
for mod in RM_FREEZE:
|
77
|
+
module = freeze_path / mod
|
78
|
+
if module.exists() and module.is_dir():
|
79
|
+
removed.append(mod)
|
80
|
+
log.debug(f"remove {mod}")
|
81
|
+
shutil.rmtree(freeze_path / mod)
|
82
|
+
elif module.exists() and module.is_file():
|
83
|
+
log.debug(f"remove {mod}")
|
84
|
+
module.unlink()
|
85
|
+
if removed:
|
86
|
+
with open(freeze_path / "removed.txt", "w") as f:
|
87
|
+
f.write("modules removed to avoid incorrect merge effects:\n")
|
88
|
+
f.write("\n".join(removed))
|
@@ -43,13 +43,20 @@ def make_path_vars(
|
|
43
43
|
raise ValueError("board path not found")
|
44
44
|
|
45
45
|
# VARS must be absolute paths
|
46
|
-
|
46
|
+
vars = {
|
47
47
|
"MPY_DIR": mpy_path.absolute().as_posix(),
|
48
48
|
"MPY_LIB_DIR": mpy_lib_path.absolute().as_posix(),
|
49
49
|
"PORT_DIR": port_path.absolute().as_posix(),
|
50
50
|
"BOARD_DIR": board_path.absolute().as_posix(),
|
51
51
|
}
|
52
52
|
|
53
|
+
if board and "ARDUINO" in board:
|
54
|
+
log.warning(f"HACK- Adding [TOP]/lib/arduino-lib to paths: {board}")
|
55
|
+
# see micropython/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.mk
|
56
|
+
vars["ARDUINO_LIB_DIR"] = (mpy_path / "lib/arduino-lib").absolute().as_posix()
|
57
|
+
|
58
|
+
return vars
|
59
|
+
|
53
60
|
|
54
61
|
def freeze_one_manifest_2(
|
55
62
|
manifest: Path, frozen_stub_path: Path, mpy_path: Path, mpy_lib_path: Path, version: str
|
stubber/freeze/get_frozen.py
CHANGED
@@ -19,13 +19,14 @@ The all_stubs folder should be mapped/symlinked to the micropython_stubs/stubs r
|
|
19
19
|
# - 1.11 and older - include content of /port/modules folder if it exists
|
20
20
|
import os
|
21
21
|
import shutil # start moving from os & glob to pathlib
|
22
|
+
import subprocess
|
22
23
|
from pathlib import Path
|
23
24
|
from typing import List, Optional
|
24
25
|
|
25
26
|
from mpflash.logger import log
|
27
|
+
from mpflash.versions import SET_PREVIEW, V_PREVIEW
|
26
28
|
from packaging.version import Version
|
27
29
|
|
28
|
-
from mpflash.versions import SET_PREVIEW, V_PREVIEW
|
29
30
|
from stubber import utils
|
30
31
|
from stubber.freeze.freeze_folder import freeze_folders # Micropython < v1.12
|
31
32
|
from stubber.freeze.freeze_manifest_2 import freeze_one_manifest_2
|
@@ -59,6 +60,8 @@ def add_comment_to_path(path: Path, comment: str) -> None:
|
|
59
60
|
pass
|
60
61
|
|
61
62
|
|
63
|
+
|
64
|
+
|
62
65
|
def freeze_any(
|
63
66
|
stub_folder: Optional[Path] = None,
|
64
67
|
version: str = V_PREVIEW,
|
stubber/merge_config.py
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
"""
|
2
|
+
Merge configuration for the stubber.
|
3
|
+
defines constants and util functions to copy, update or remove type modules
|
4
|
+
"""
|
5
|
+
|
6
|
+
import shutil
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Final, List
|
9
|
+
|
10
|
+
from mpflash.logger import log
|
11
|
+
from stubber.rst.lookup import U_MODULES
|
12
|
+
|
13
|
+
EXT: Final = [".pyi", ".py", ""]
|
14
|
+
CP_REFERENCE_TO_DOCSTUB: Final = [
|
15
|
+
"asyncio",
|
16
|
+
"rp2/PIOASMEmit.pyi",
|
17
|
+
"rp2/asm_pio.pyi",
|
18
|
+
]
|
19
|
+
"Modules to copy from reference modules to the docstubs"
|
20
|
+
|
21
|
+
|
22
|
+
STDLIB_MODULES: Final = [
|
23
|
+
"collections",
|
24
|
+
"io",
|
25
|
+
"builtins",
|
26
|
+
"asyncio",
|
27
|
+
"sys",
|
28
|
+
# "os", # TODO # Do not remove `os` to allow better typing by mypy for the `os` module
|
29
|
+
# "ssl", # TODO
|
30
|
+
]
|
31
|
+
"""Modules that should be in /stdlib"""
|
32
|
+
# and should not be in the individual packes as that causes duplication
|
33
|
+
|
34
|
+
RM_MERGED: Final = (
|
35
|
+
[
|
36
|
+
"sys", # use auto patched version from mpy_stdlib
|
37
|
+
"asyncio", # use manually patched version from mpy_stdlib
|
38
|
+
"_asyncio", # ditto
|
39
|
+
"uasyncio", # ditto
|
40
|
+
"_rp2", # Leave out for now , to avoid conflicts with the rp2 module
|
41
|
+
"pycopy_imphook", # pycopy only: not needed in the merged stubs
|
42
|
+
# "os",
|
43
|
+
]
|
44
|
+
+ STDLIB_MODULES
|
45
|
+
+ [f"u{mod}" for mod in U_MODULES]
|
46
|
+
)
|
47
|
+
"Modules to remove from merged stubs, U_MODULES will be recreated later"
|
48
|
+
|
49
|
+
|
50
|
+
def copy_type_modules(source_folder: Path, target_folder: Path, CP_REFERENCE_MODULES: List[str]):
|
51
|
+
log.info("Adding additional type modules:")
|
52
|
+
for addition in CP_REFERENCE_MODULES:
|
53
|
+
src = source_folder / addition
|
54
|
+
if src.exists():
|
55
|
+
if src.is_dir():
|
56
|
+
target = target_folder / addition
|
57
|
+
log.debug(f" - add {target}")
|
58
|
+
shutil.copytree(src, target, dirs_exist_ok=True)
|
59
|
+
else:
|
60
|
+
target = target_folder / addition
|
61
|
+
log.debug(f" - add {target}")
|
62
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
63
|
+
shutil.copy2(src, target)
|
64
|
+
|
65
|
+
|
66
|
+
def recreate_umodules(target_folder: Path):
|
67
|
+
log.info("create umodules to refer to modules in the merged stubs")
|
68
|
+
# Just `create an import * from module` in the umodule.pyi
|
69
|
+
for name in U_MODULES:
|
70
|
+
# delete complex or simple umodule
|
71
|
+
uname = target_folder / f"u{name}"
|
72
|
+
try:
|
73
|
+
if uname.exists():
|
74
|
+
if uname.is_dir():
|
75
|
+
log.debug(f" - remove {uname}")
|
76
|
+
shutil.rmtree(uname)
|
77
|
+
else:
|
78
|
+
log.debug(f" - remove {uname}")
|
79
|
+
uname.unlink()
|
80
|
+
else:
|
81
|
+
uname = uname.with_suffix(".pyi")
|
82
|
+
if uname.exists():
|
83
|
+
log.debug(f" - remove {uname}")
|
84
|
+
uname.unlink()
|
85
|
+
except OSError as e:
|
86
|
+
log.error(f"Error removing {uname}: {e}")
|
87
|
+
continue
|
88
|
+
|
89
|
+
uname = target_folder / f"u{name}.pyi"
|
90
|
+
with uname.open("w") as f:
|
91
|
+
f.write(f"# This umodule is a MicroPython reference to {name}\n")
|
92
|
+
f.write(f"from {name} import *\n")
|
93
|
+
log.debug(f" - recreated {uname.name}")
|
94
|
+
|
95
|
+
|
96
|
+
def remove_modules(target_folder: Path, RM_MODULES: List[str]):
|
97
|
+
log.info("Removing modules from the merged stubs")
|
98
|
+
|
99
|
+
for name in RM_MODULES:
|
100
|
+
for ext in EXT:
|
101
|
+
target = target_folder / f"{name}{ext}"
|
102
|
+
if target.exists():
|
103
|
+
try:
|
104
|
+
if target.is_dir():
|
105
|
+
log.debug(f" - remove {target}")
|
106
|
+
shutil.rmtree(target)
|
107
|
+
else:
|
108
|
+
log.debug(f" - remove {target}")
|
109
|
+
target.unlink()
|
110
|
+
finally:
|
111
|
+
log.debug(f" - remove {target}")
|
stubber/minify.py
CHANGED
@@ -17,7 +17,6 @@ except ImportError:
|
|
17
17
|
python_minifier = None
|
18
18
|
|
19
19
|
from mpflash.logger import log
|
20
|
-
|
21
20
|
from mpflash.versions import SET_PREVIEW, V_PREVIEW
|
22
21
|
|
23
22
|
# Type Aliases for minify
|
@@ -372,7 +371,7 @@ def cross_compile(
|
|
372
371
|
_target = get_temp_file(suffix=".mpy")
|
373
372
|
result = pipx_mpy_cross(version, source_file, _target)
|
374
373
|
if result.stderr and "No matching distribution found for mpy-cross==" in result.stderr:
|
375
|
-
log.warning(f"mpy-cross=={version} not found, using
|
374
|
+
log.warning(f"mpy-cross=={version} not found, using most current version.")
|
376
375
|
result = pipx_mpy_cross(V_PREVIEW, source_file, _target)
|
377
376
|
|
378
377
|
if result.returncode == 0:
|
stubber/publish/database.py
CHANGED
@@ -1,18 +1,59 @@
|
|
1
|
-
"""
|
1
|
+
"""Interface to the database which stores the package information"""
|
2
2
|
|
3
|
+
import sqlite3
|
3
4
|
from pathlib import Path
|
4
|
-
from typing import Union
|
5
5
|
|
6
|
-
from pysondb import PysonDB
|
7
6
|
|
7
|
+
def get_database(db_path: Path, production: bool = False) -> sqlite3.Connection:
|
8
|
+
"""
|
9
|
+
Open the sqlite database at the given path.
|
10
|
+
|
11
|
+
The database should be located in a subfolder `/data` of the root path.
|
12
|
+
The database name is determined by the production flag as `all_packages[_test].db`
|
13
|
+
"""
|
14
|
+
db_path = Path(db_path)
|
15
|
+
if db_path.stem == "publish":
|
16
|
+
db_path = db_path / ".." / "data" # go up one level to find the database
|
17
|
+
|
18
|
+
db_path = db_path / f"all_packages{'' if production else '_test'}.db"
|
19
|
+
if db_path.exists():
|
20
|
+
conn = sqlite3.connect(db_path)
|
8
21
|
|
9
|
-
|
22
|
+
else:
|
23
|
+
print(FileNotFoundError(f"Database file not found in path: {db_path}"))
|
24
|
+
conn = create_database(db_path)
|
25
|
+
|
26
|
+
conn.row_factory = sqlite3.Row # return rows as dicts
|
27
|
+
return conn
|
28
|
+
|
29
|
+
|
30
|
+
def create_database(db_path: Path) -> sqlite3.Connection:
|
10
31
|
"""
|
11
|
-
|
32
|
+
Create a new database at the given path.
|
12
33
|
|
13
|
-
The database should be located in a subfolder `/
|
14
|
-
|
34
|
+
The database should be located in a subfolder `/data` of the root path.
|
35
|
+
"""
|
36
|
+
db_path = Path(db_path)
|
37
|
+
if not db_path.parent.exists():
|
38
|
+
db_path.parent.mkdir(parents=True)
|
39
|
+
conn = sqlite3.connect(db_path)
|
40
|
+
SCHEMA = """
|
41
|
+
CREATE TABLE "packages" (
|
42
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
43
|
+
name TEXT,
|
44
|
+
description TEXT,
|
45
|
+
mpy_version TEXT,
|
46
|
+
pkg_version TEXT,
|
47
|
+
publish BOOLEAN,
|
48
|
+
stub_sources TEXT, -- json string
|
49
|
+
path TEXT,
|
50
|
+
hash TEXT,
|
51
|
+
stub_hash TEXT,
|
52
|
+
port TEXT DEFAULT "",
|
53
|
+
board TEXT DEFAULT "",
|
54
|
+
variant TEXT DEFAULT ""
|
55
|
+
)
|
15
56
|
"""
|
16
|
-
|
17
|
-
|
18
|
-
return
|
57
|
+
conn.execute(SCHEMA)
|
58
|
+
conn.commit
|
59
|
+
return conn
|
@@ -9,9 +9,9 @@ from typing import List, Optional, Union
|
|
9
9
|
from mpflash.logger import log
|
10
10
|
|
11
11
|
from stubber.codemod.enrich import enrich_folder
|
12
|
+
from stubber.merge_config import RM_MERGED, recreate_umodules, remove_modules
|
12
13
|
from stubber.publish.candidates import board_candidates, filter_list
|
13
14
|
from stubber.publish.defaults import GENERIC, GENERIC_L, default_board
|
14
|
-
from stubber.publish.missing_class_methods import add_machine_pin_call
|
15
15
|
from stubber.publish.pathnames import get_base, get_board_path, get_merged_path
|
16
16
|
from stubber.utils.config import CONFIG
|
17
17
|
|
@@ -63,14 +63,11 @@ def merge_all_docstubs(
|
|
63
63
|
log.warning(f"No docstubs found for {candidate['version']}")
|
64
64
|
continue
|
65
65
|
if not board_path.exists():
|
66
|
-
log.
|
66
|
+
log.debug(f"skipping {merged_path.name}, no MCU stubs found in {board_path}")
|
67
67
|
continue
|
68
68
|
log.info(f"Merge {candidate['version']} docstubs with boardstubs to {merged_path.name}")
|
69
69
|
try:
|
70
70
|
result = copy_and_merge_docstubs(board_path, merged_path, doc_path)
|
71
|
-
# Add methods from docstubs to the MCU stubs that do not exist in the MCU stubs
|
72
|
-
# Add the __call__ method to the machine.Pin and pyb.Pin class
|
73
|
-
add_machine_pin_call(merged_path, candidate["version"])
|
74
71
|
except Exception as e:
|
75
72
|
log.error(f"Error parsing {candidate['version']} docstubs: {e}")
|
76
73
|
continue
|
@@ -83,9 +80,10 @@ def merge_all_docstubs(
|
|
83
80
|
def copy_and_merge_docstubs(fw_path: Path, dest_path: Path, docstub_path: Path):
|
84
81
|
"""
|
85
82
|
Parameters:
|
86
|
-
fw_path: Path to MCU stubs (absolute path)
|
83
|
+
fw_path: Path to the source MCU stubs (absolute path)
|
87
84
|
dest_path: Path to destination (absolute path)
|
88
|
-
|
85
|
+
docstub_path: Path to docstubs
|
86
|
+
|
89
87
|
|
90
88
|
Copy files from the firmware stub folders to the merged
|
91
89
|
- 1 - Copy all MCU stubs to the package folder
|
@@ -118,19 +116,38 @@ def copy_and_merge_docstubs(fw_path: Path, dest_path: Path, docstub_path: Path):
|
|
118
116
|
if (dest_path / f.name).with_suffix(suffix).exists():
|
119
117
|
(dest_path / f.name).with_suffix(suffix).unlink()
|
120
118
|
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
]:
|
126
|
-
for suffix in [".py", ".pyi"]:
|
127
|
-
if (dest_path / name).with_suffix(suffix).exists(): # type: ignore
|
128
|
-
(dest_path / name).with_suffix(suffix).unlink() # type: ignore
|
119
|
+
# remove unwanted modules
|
120
|
+
remove_modules(dest_path, RM_MERGED)
|
121
|
+
# fixup the umodules
|
122
|
+
recreate_umodules(dest_path)
|
129
123
|
|
130
124
|
# 2 - Enrich the MCU stubs with the document stubs
|
131
|
-
result = enrich_folder(
|
125
|
+
result = enrich_folder(source_folder=docstub_path, target_folder=dest_path, write_back=True)
|
126
|
+
|
127
|
+
refactor_rp2_module(dest_path)
|
132
128
|
|
133
129
|
# copy the docstubs manifest.json file to the package folder
|
134
130
|
if (docstub_path / "modules.json").exists():
|
135
131
|
shutil.copy(docstub_path / "modules.json", dest_path / "doc_stubs.json")
|
136
132
|
return result
|
133
|
+
|
134
|
+
|
135
|
+
def refactor_rp2_module(dest_path: Path):
|
136
|
+
"""refactor the rp2 module to allow for submodules"""
|
137
|
+
rp2_file = dest_path / "rp2.pyi"
|
138
|
+
if not rp2_file.exists():
|
139
|
+
# not a rp2
|
140
|
+
return
|
141
|
+
|
142
|
+
log.info(f"refactor rps module stub")
|
143
|
+
rp2_folder = dest_path / "rp2"
|
144
|
+
rp2_folder.mkdir(exist_ok=True)
|
145
|
+
if not (rp2_folder / "__init__.pyi").exists():
|
146
|
+
# do not overwrite docstubs __init__.pyi
|
147
|
+
rp2_file.rename(rp2_folder / "__init__.pyi")
|
148
|
+
# copy the asm_pio.pyi file from the reference folder
|
149
|
+
for submod in ["rp2/asm_pio.pyi"]:
|
150
|
+
file = CONFIG.mpy_stubs_path / "micropython-reference" / submod
|
151
|
+
if file.exists():
|
152
|
+
shutil.copy(file, rp2_folder / file.name)
|
153
|
+
log.info(f" - add rp2/{ file.name}")
|
stubber/publish/package.py
CHANGED
@@ -3,15 +3,14 @@ prepare a set of stub files for publishing to PyPi
|
|
3
3
|
|
4
4
|
"""
|
5
5
|
|
6
|
+
import sqlite3
|
6
7
|
import sys
|
7
8
|
from pathlib import Path
|
8
9
|
from typing import Dict, Union
|
9
10
|
|
10
11
|
from mpflash.logger import log
|
11
|
-
from packaging.version import parse
|
12
|
-
from pysondb import PysonDB
|
13
|
-
|
14
12
|
from mpflash.versions import clean_version
|
13
|
+
|
15
14
|
from stubber.publish.defaults import GENERIC, GENERIC_L, default_board
|
16
15
|
from stubber.publish.enums import StubSource
|
17
16
|
from stubber.publish.stubpackage import StubPackage, StubSources
|
@@ -33,7 +32,7 @@ def package_name(*, port: str = "", board: str = "", family: str = "micropython"
|
|
33
32
|
|
34
33
|
|
35
34
|
def get_package(
|
36
|
-
|
35
|
+
db_conn: sqlite3.Connection,
|
37
36
|
*,
|
38
37
|
version: str,
|
39
38
|
port: str,
|
@@ -44,13 +43,20 @@ def get_package(
|
|
44
43
|
pkg_name = package_name(port=port, board=board, family=family)
|
45
44
|
version = clean_version(version, drop_v=True)
|
46
45
|
if package_info := get_package_info(
|
47
|
-
|
46
|
+
db_conn,
|
48
47
|
CONFIG.publish_path,
|
49
48
|
pkg_name=pkg_name,
|
50
49
|
mpy_version=version,
|
51
50
|
):
|
52
51
|
# create package from the information retrieved from the database
|
53
|
-
|
52
|
+
p_db = StubPackage(
|
53
|
+
pkg_name,
|
54
|
+
port,
|
55
|
+
board=board,
|
56
|
+
version=version,
|
57
|
+
json_data=package_info,
|
58
|
+
)
|
59
|
+
return p_db
|
54
60
|
|
55
61
|
log.debug(f"No package found for {pkg_name} in database, creating new package")
|
56
62
|
return create_package(
|
@@ -63,29 +69,37 @@ def get_package(
|
|
63
69
|
|
64
70
|
|
65
71
|
def get_package_info(
|
66
|
-
|
72
|
+
db_conn: sqlite3.Connection,
|
73
|
+
pub_path: Path,
|
74
|
+
*,
|
75
|
+
pkg_name: str,
|
76
|
+
mpy_version: str,
|
67
77
|
) -> Union[Dict, None]:
|
68
78
|
"""
|
69
79
|
get a package's record from the json db if it can be found
|
70
|
-
matches
|
80
|
+
matches on the package name and version
|
71
81
|
pkg_name: package name (micropython-esp32-stubs)
|
72
82
|
mpy_version: micropython/firmware version (1.18)
|
73
83
|
"""
|
74
84
|
# find in the database
|
75
|
-
|
76
|
-
|
85
|
+
|
86
|
+
cursor = db_conn.cursor()
|
87
|
+
cursor.execute(
|
88
|
+
"""
|
89
|
+
SELECT * FROM packages
|
90
|
+
WHERE name = ? AND mpy_version LIKE ?
|
91
|
+
ORDER BY pkg_version DESC
|
92
|
+
""",
|
93
|
+
(pkg_name, f"{mpy_version}%"),
|
77
94
|
)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
95
|
+
packages = [dict(row) for row in cursor.fetchall()]
|
96
|
+
|
97
|
+
if packages:
|
98
|
+
pkg_from_db = packages[0]
|
82
99
|
|
83
|
-
if len(packages) > 0:
|
84
|
-
pkg_from_db = packages[-1]["data"]
|
85
100
|
log.debug(f"Found latest {pkg_name} == {pkg_from_db['pkg_version']}")
|
86
101
|
return pkg_from_db
|
87
|
-
|
88
|
-
return None
|
102
|
+
return None
|
89
103
|
|
90
104
|
|
91
105
|
def create_package(
|