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.
Files changed (70) hide show
  1. {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/METADATA +30 -12
  2. {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/RECORD +69 -66
  3. {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/WHEEL +1 -1
  4. mpflash/README.md +2 -2
  5. mpflash/mpflash/basicgit.py +49 -9
  6. mpflash/mpflash/common.py +23 -16
  7. mpflash/mpflash/downloaded.py +10 -2
  8. mpflash/mpflash/mpboard_id/__init__.py +9 -4
  9. mpflash/mpflash/mpboard_id/add_boards.py +25 -14
  10. mpflash/mpflash/mpboard_id/board.py +2 -2
  11. mpflash/mpflash/mpboard_id/board_id.py +10 -6
  12. mpflash/mpflash/mpboard_id/board_info.zip +0 -0
  13. mpflash/mpflash/mpboard_id/store.py +8 -3
  14. mpflash/mpflash/mpremoteboard/__init__.py +13 -8
  15. mpflash/mpflash/mpremoteboard/mpy_fw_info.py +27 -16
  16. mpflash/mpflash/vendor/board_database.py +185 -0
  17. mpflash/mpflash/vendor/readme.md +10 -1
  18. mpflash/mpflash/versions.py +28 -40
  19. mpflash/poetry.lock +1605 -601
  20. mpflash/pyproject.toml +4 -3
  21. stubber/__init__.py +1 -1
  22. stubber/board/createstubs.py +51 -27
  23. stubber/board/createstubs_db.py +36 -28
  24. stubber/board/createstubs_db_min.py +171 -165
  25. stubber/board/createstubs_db_mpy.mpy +0 -0
  26. stubber/board/createstubs_mem.py +36 -28
  27. stubber/board/createstubs_mem_min.py +184 -178
  28. stubber/board/createstubs_mem_mpy.mpy +0 -0
  29. stubber/board/createstubs_min.py +102 -94
  30. stubber/board/createstubs_mpy.mpy +0 -0
  31. stubber/board/modulelist.txt +16 -0
  32. stubber/codemod/enrich.py +297 -88
  33. stubber/codemod/merge_docstub.py +250 -65
  34. stubber/codemod/test_enrich.py +87 -0
  35. stubber/codemod/visitors/typevars.py +200 -0
  36. stubber/commands/build_cmd.py +16 -3
  37. stubber/commands/clone_cmd.py +3 -3
  38. stubber/commands/config_cmd.py +4 -2
  39. stubber/commands/enrich_folder_cmd.py +33 -21
  40. stubber/commands/get_core_cmd.py +1 -2
  41. stubber/commands/get_docstubs_cmd.py +60 -6
  42. stubber/commands/get_frozen_cmd.py +15 -12
  43. stubber/commands/get_mcu_cmd.py +3 -3
  44. stubber/commands/merge_cmd.py +1 -2
  45. stubber/commands/publish_cmd.py +19 -4
  46. stubber/commands/stub_cmd.py +3 -3
  47. stubber/commands/switch_cmd.py +3 -5
  48. stubber/commands/variants_cmd.py +3 -3
  49. stubber/cst_transformer.py +52 -17
  50. stubber/freeze/common.py +27 -11
  51. stubber/freeze/freeze_manifest_2.py +8 -1
  52. stubber/freeze/get_frozen.py +4 -1
  53. stubber/merge_config.py +111 -0
  54. stubber/minify.py +1 -2
  55. stubber/publish/database.py +51 -10
  56. stubber/publish/merge_docstubs.py +33 -16
  57. stubber/publish/package.py +32 -18
  58. stubber/publish/publish.py +8 -8
  59. stubber/publish/stubpackage.py +110 -47
  60. stubber/rst/lookup.py +205 -43
  61. stubber/rst/reader.py +106 -59
  62. stubber/rst/rst_utils.py +24 -11
  63. stubber/stubber.py +1 -1
  64. stubber/stubs_from_docs.py +31 -13
  65. stubber/update_module_list.py +2 -2
  66. stubber/utils/config.py +33 -13
  67. stubber/utils/post.py +9 -6
  68. stubber/publish/missing_class_methods.py +0 -51
  69. {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/LICENSE +0 -0
  70. {micropython_stubber-1.23.2.dist-info → micropython_stubber-1.24.0.dist-info}/entry_points.txt +0 -0
@@ -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(
@@ -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 functiondefs and classdefs info read from the stubs source"
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 = "?" # funcdef or classdef or module
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
- MODDOC_KEY = ("__module_docstring",)
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
- Union[TypeInfo, str],
60
+ AnnoValue, # The TypeInfo or list of TypeInfo
48
61
  ] = {}
49
- self.comments :List[str] = []
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
- self.annotations[tuple(self.stack)] = ti
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
- src_comment: Optional[cst.SimpleStatementLine],
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 functiondefs ending in an ellipsis, the entire body needs to be replaced.
133
- in this case the src_body is mandatory.
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 src_comment:
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 funcky casting to avoid issues with changing the body
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([src_comment] + list(dest_node.body.body))
186
+ body = tuple([src_docstr] + list(dest_node.body.body))
152
187
  else:
153
- body = tuple([src_comment] + list(dest_node.body.body[1:]))
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 / "uasyncio").exists() and not (freeze_path / "uasyncio" / "task.py").exists():
65
- # copy task.py from micropython\extmod\uasyncio\task.py to stub_folder
66
- log.debug("add missing : uasyncio/task.py")
67
- task_py = mpy_path / "extmod" / "uasyncio" / "task.py"
68
- try:
69
- shutil.copy(str(task_py), str(freeze_path / "uasyncio"))
70
- except OSError as er:
71
- log.warning(f"error copying {task_py} : {er}")
72
- # try to continue
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
- return {
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
@@ -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,
@@ -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 default version.")
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:
@@ -1,18 +1,59 @@
1
- """basic interface to the json database"""
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
- def get_database(publish_path: Union[Path, str], production: bool = False) -> PysonDB:
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
- Open the json database at the given path.
32
+ Create a new database at the given path.
12
33
 
13
- The database should be located in a subfolder `/publish` of the root path.
14
- The database name is determined by the production flag as `package_data[_test].jsondb`
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
- publish_path = Path(publish_path)
17
- db_path = publish_path / f"package_data{'' if production else '_test'}.jsondb"
18
- return PysonDB(db_path.as_posix())
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.info(f"skipping {merged_path.name}, no MCU stubs found in {board_path}")
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
- mpy_version: micropython version ('1.18')
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
- # delete builtins.pyi in the package folder
122
- for name in [
123
- "builtins", # creates conflicts, better removed
124
- "pycopy_imphook", # is not intended to be used directly, and has an unresolved subclass
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(dest_path, docstub_path=docstub_path, write_back=True)
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}")
@@ -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
- db: PysonDB,
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
- db,
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
- return StubPackage(pkg_name, port, board=board, version=version, json_data=package_info)
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
- db: PysonDB, pub_path: Path, *, pkg_name: str, mpy_version: str
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 om the package name and version
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
- recs = db.get_by_query(
76
- query=lambda x: x["mpy_version"] == mpy_version and x["name"] == pkg_name
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
- # dict to list
79
- recs = [{"id": key, "data": recs[key]} for key in recs]
80
- # sort
81
- packages = sorted(recs, key=lambda x: parse(x["data"]["pkg_version"]))
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
- else:
88
- return None
102
+ return None
89
103
 
90
104
 
91
105
  def create_package(