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/rst/reader.py
CHANGED
@@ -69,10 +69,12 @@ from typing import List, Optional, Tuple
|
|
69
69
|
|
70
70
|
from mpflash.logger import log
|
71
71
|
from mpflash.versions import V_PREVIEW
|
72
|
+
|
72
73
|
from stubber.rst import (
|
73
74
|
CHILD_PARENT_CLASS,
|
74
75
|
MODULE_GLUE,
|
75
76
|
PARAM_FIXES,
|
77
|
+
PARAM_RE_FIXES,
|
76
78
|
RST_DOC_FIXES,
|
77
79
|
TYPING_IMPORT,
|
78
80
|
ClassSourceDict,
|
@@ -83,14 +85,17 @@ from stubber.rst import (
|
|
83
85
|
from stubber.rst.lookup import Fix
|
84
86
|
from stubber.utils.config import CONFIG
|
85
87
|
|
86
|
-
|
88
|
+
SEPARATOR = "::"
|
89
|
+
USE_SUBMODULES = True
|
87
90
|
|
88
91
|
|
89
92
|
class FileReadWriter:
|
90
93
|
"""base class for reading rst files"""
|
91
94
|
|
92
95
|
def __init__(self):
|
93
|
-
self.filename = ""
|
96
|
+
self.filename: str = ""
|
97
|
+
# self.modulename: str = ""
|
98
|
+
# self.out_file: str = "__init__.pyi"
|
94
99
|
# input buffer
|
95
100
|
self.rst_text: List[str] = []
|
96
101
|
self.max_line = 0
|
@@ -111,6 +116,8 @@ class FileReadWriter:
|
|
111
116
|
# some lines now may have \n sin them , so re-join and re-split the lines
|
112
117
|
self.rst_text = "".join(self.rst_text).splitlines(keepends=True)
|
113
118
|
|
119
|
+
self.modulename = filename.stem
|
120
|
+
self.out_file = "__init__.pyi"
|
114
121
|
self.filename = filename.as_posix() # use fwd slashes in origin
|
115
122
|
self.max_line = len(self.rst_text) - 1
|
116
123
|
|
@@ -148,7 +155,11 @@ class FileReadWriter:
|
|
148
155
|
"""
|
149
156
|
append = 0
|
150
157
|
newline = self.rst_text[self.line_no]
|
151
|
-
while
|
158
|
+
while (
|
159
|
+
not self.is_balanced(newline)
|
160
|
+
and self.line_no >= 0
|
161
|
+
and (self.line_no + append + 1) <= self.max_line
|
162
|
+
):
|
152
163
|
append += 1
|
153
164
|
# concat the lines
|
154
165
|
newline += self.rst_text[self.line_no + append]
|
@@ -177,6 +188,7 @@ class RSTReader(FileReadWriter):
|
|
177
188
|
self.current_module = ""
|
178
189
|
self.current_class = ""
|
179
190
|
self.current_function = "" # function & method
|
191
|
+
self.clean_rst = True
|
180
192
|
super().__init__()
|
181
193
|
|
182
194
|
def read_file(self, filename: Path):
|
@@ -215,11 +227,13 @@ class RSTReader(FileReadWriter):
|
|
215
227
|
# return _l.startswith("..") and not any(_l.startswith(a) for a in self.docstring_anchors)
|
216
228
|
|
217
229
|
# @property
|
218
|
-
def at_heading(self, large=False) -> bool:
|
230
|
+
def at_heading(self, large: bool = False) -> bool:
|
219
231
|
"stop at heading"
|
220
232
|
u_line = self.rst_text[min(self.line_no + 1, self.max_line - 1)].rstrip()
|
221
233
|
# Heading ---, ==, ~~~
|
222
|
-
underlined =
|
234
|
+
underlined = (
|
235
|
+
u_line.startswith("---") or u_line.startswith("===") or u_line.startswith("~~~")
|
236
|
+
)
|
223
237
|
if underlined and self.line_no > 0:
|
224
238
|
# check if previous line is a heading
|
225
239
|
line = self.rst_text[self.line_no].strip()
|
@@ -278,22 +292,23 @@ class RSTReader(FileReadWriter):
|
|
278
292
|
# remove empty lines at beginning/end of block
|
279
293
|
block = self.clean_docstr(block)
|
280
294
|
# add clickable hyperlinks to CPython docpages
|
281
|
-
block = self.
|
295
|
+
block = self.add_link_to_docstr(block)
|
282
296
|
# make sure the first char of the first line is a capital
|
283
297
|
if len(block) > 0 and len(block[0]) > 0:
|
284
298
|
block[0] = block[0][0].upper() + block[0][1:]
|
285
299
|
return block
|
286
300
|
|
287
|
-
@staticmethod
|
288
|
-
def clean_docstr(block: List[str]):
|
301
|
+
# @staticmethod
|
302
|
+
def clean_docstr(self, block: List[str]):
|
289
303
|
"""Clean up a docstring"""
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
304
|
+
if self.clean_rst:
|
305
|
+
# if a Quoted Literal Block , then remove the first character of each line
|
306
|
+
# https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#quoted-literal-blocks
|
307
|
+
if block and len(block[0]) > 0 and block[0][0] != " ":
|
308
|
+
q_char = block[0][0]
|
309
|
+
if all(l.startswith(q_char) for l in block):
|
310
|
+
# all lines start with the same character, so skip that character
|
311
|
+
block = [l[1:] for l in block]
|
297
312
|
# rstrip all lines
|
298
313
|
block = [l.rstrip() for l in block]
|
299
314
|
# remove empty lines at beginning/end of block
|
@@ -301,7 +316,6 @@ class RSTReader(FileReadWriter):
|
|
301
316
|
block = block[1:]
|
302
317
|
while len(block) and len(block[-1]) == 0:
|
303
318
|
block = block[:-1]
|
304
|
-
|
305
319
|
# Clean up Synopsis
|
306
320
|
if len(block) and ":synopsis:" in block[0]:
|
307
321
|
block[0] = re.sub(
|
@@ -311,15 +325,18 @@ class RSTReader(FileReadWriter):
|
|
311
325
|
)
|
312
326
|
return block
|
313
327
|
|
314
|
-
|
315
|
-
|
316
|
-
|
328
|
+
def add_link_to_docstr(self, block: List[str]):
|
329
|
+
"""Add clickable hyperlinks to CPython docpages,
|
330
|
+
and clean the docstring from rst constructs"""
|
331
|
+
if not self.clean_rst:
|
332
|
+
return block
|
333
|
+
|
317
334
|
for i in range(len(block)):
|
318
335
|
# hyperlink to Cpython doc pages
|
319
336
|
# https://regex101.com/r/5RN8rj/1
|
320
337
|
# Link to python 3 documentation
|
321
338
|
_l = re.sub(
|
322
|
-
r"(\s*\|see_cpython_module\|\s+:mod:`python:(?P<mod>[\w
|
339
|
+
r"(\s*\|see_cpython_module\|\s+:mod:`python:(?P<mod>[\w|]*)\s?`)[.]?",
|
323
340
|
r"\g<1> https://docs.python.org/3/library/\g<mod>.html .",
|
324
341
|
block[i],
|
325
342
|
)
|
@@ -333,7 +350,10 @@ class RSTReader(FileReadWriter):
|
|
333
350
|
# Clean up note and other docstring anchors
|
334
351
|
_l = _l.replace(".. note:: ", "``Note:`` ")
|
335
352
|
_l = _l.replace(".. data:: ", "")
|
336
|
-
_l = _l.replace(".. admonition:: ", "")
|
353
|
+
_l = _l.replace(".. admonition:: ", "Admonition:")
|
354
|
+
# remove rst highlights from docstrings
|
355
|
+
_l = _l.replace(":class: attention\n", "")
|
356
|
+
# Sphinx directive to link to CPython documentation
|
337
357
|
_l = _l.replace("|see_cpython_module|", "CPython module:")
|
338
358
|
# clean up unsupported escape sequences in rst
|
339
359
|
_l = _l.replace(r"\ ", " ")
|
@@ -347,7 +367,7 @@ class RSTReader(FileReadWriter):
|
|
347
367
|
return m[1] if m else ""
|
348
368
|
|
349
369
|
def strip_prefixes(self, name: str, strip_mod: bool = True, strip_class: bool = False):
|
350
|
-
"Remove the modulename. and or the classname. from the
|
370
|
+
"Remove the modulename. and or the classname. from the beginning of a name"
|
351
371
|
prefixes = self.module_names if strip_mod else []
|
352
372
|
if strip_class and self.current_class != "":
|
353
373
|
prefixes += [self.current_class]
|
@@ -364,12 +384,6 @@ class RSTParser(RSTReader):
|
|
364
384
|
"""
|
365
385
|
|
366
386
|
target = ".py" # py/pyi
|
367
|
-
# TODO: Move to lookup.py
|
368
|
-
PARAM_RE_FIXES = [
|
369
|
-
Fix(r"\[angle, time=0\]", "[angle], time=0", is_re=True), # fix: method:: Servo.angle([angle, time=0])
|
370
|
-
Fix(r"\[speed, time=0\]", "[speed], time=0", is_re=True), # fix: .. method:: Servo.speed([speed, time=0])
|
371
|
-
Fix(r"\[service_id, key=None, \*, \.\.\.\]", "[service_id], [key], *, ...", is_re=True), # fix: network - AbstractNIC.connect
|
372
|
-
]
|
373
387
|
|
374
388
|
def __init__(self, v_tag: str) -> None:
|
375
389
|
super().__init__()
|
@@ -398,7 +412,7 @@ class RSTParser(RSTReader):
|
|
398
412
|
|
399
413
|
## Deal with SQUARE brackets first ( Documentation meaning := [optional])
|
400
414
|
|
401
|
-
for fix in
|
415
|
+
for fix in PARAM_RE_FIXES:
|
402
416
|
params = self.apply_fix(fix, params, name)
|
403
417
|
|
404
418
|
# ###########################################################################################################
|
@@ -432,7 +446,9 @@ class RSTParser(RSTReader):
|
|
432
446
|
def apply_fix(fix: Fix, params: str, name: str = ""):
|
433
447
|
if fix.name and fix.name != name:
|
434
448
|
return params
|
435
|
-
return
|
449
|
+
return (
|
450
|
+
re.sub(fix.from_, fix.to, params) if fix.is_re else params.replace(fix.from_, fix.to)
|
451
|
+
)
|
436
452
|
|
437
453
|
def create_update_class(self, name: str, params: str, docstr: List[str]):
|
438
454
|
# a bit of a hack: assume no classes in classes or functions in function
|
@@ -442,7 +458,9 @@ class RSTParser(RSTReader):
|
|
442
458
|
class_def = self.output_dict[full_name]
|
443
459
|
else:
|
444
460
|
parent = CHILD_PARENT_CLASS[name] if name in CHILD_PARENT_CLASS.keys() else ""
|
445
|
-
if parent == "" and (
|
461
|
+
if parent == "" and (
|
462
|
+
name.endswith("Error") or name.endswith("Exception") or name in {"GeneratorExit"}
|
463
|
+
):
|
446
464
|
parent = "Exception"
|
447
465
|
class_def = ClassSourceDict(
|
448
466
|
f"class {name}({parent}):",
|
@@ -467,12 +485,25 @@ class RSTParser(RSTReader):
|
|
467
485
|
toctree = self.read_docstring()
|
468
486
|
# cleanup toctree
|
469
487
|
toctree = [x.strip() for x in toctree if f"{self.current_module}." in x]
|
470
|
-
|
471
|
-
|
472
|
-
#
|
473
|
-
|
474
|
-
|
475
|
-
|
488
|
+
|
489
|
+
if USE_SUBMODULES:
|
490
|
+
# add sub modules imports
|
491
|
+
for file in toctree:
|
492
|
+
submod_name = file.replace(".rst", "")
|
493
|
+
rel_name = submod_name.replace(f"{self.modulename}.", ".")
|
494
|
+
assumed_class = rel_name.lstrip(".") # .capitalize()
|
495
|
+
|
496
|
+
# absolute class import with class name based on the module name
|
497
|
+
self.output_dict.add_import(f"from {submod_name} import {assumed_class}")
|
498
|
+
|
499
|
+
else:
|
500
|
+
# Now parse all files mentioned in the toc
|
501
|
+
# sub modules are now processed as individual files
|
502
|
+
for file in toctree:
|
503
|
+
#
|
504
|
+
file_path = CONFIG.mpy_path / "docs" / "library" / file.strip()
|
505
|
+
self.read_file(file_path)
|
506
|
+
self.parse()
|
476
507
|
# reset this file to done
|
477
508
|
self.rst_text = []
|
478
509
|
self.line_no = 1
|
@@ -480,7 +511,7 @@ class RSTParser(RSTReader):
|
|
480
511
|
def parse_module(self):
|
481
512
|
"parse a module tag and set the module's docstring"
|
482
513
|
log.trace(f"# {self.line.rstrip()}")
|
483
|
-
module_name = self.line.split(
|
514
|
+
module_name = self.line.split(SEPARATOR)[-1].strip()
|
484
515
|
|
485
516
|
self.current_module = module_name
|
486
517
|
self.current_function = self.current_class = ""
|
@@ -493,31 +524,40 @@ class RSTParser(RSTReader):
|
|
493
524
|
if "nightly" in self.source_tag:
|
494
525
|
version = V_PREVIEW
|
495
526
|
else:
|
496
|
-
version = self.source_tag.replace("_", ".")
|
497
|
-
|
527
|
+
version = self.source_tag.replace("_", ".")
|
528
|
+
# documentation is not available for patch versions, so use only the major.minor.0
|
529
|
+
version = ".".join(version.split(".")[:2]) + ".0"
|
530
|
+
# TODO Use clean_version(self.source_tag)
|
531
|
+
docstr[0] = (
|
532
|
+
f"{docstr[0]}.\n\nMicroPython module: https://docs.micropython.org/en/{version}/library/{module_name}.html"
|
533
|
+
)
|
498
534
|
|
499
535
|
self.output_dict.name = module_name
|
500
536
|
self.output_dict.add_comment(f"# source version: {self.source_tag}")
|
501
537
|
self.output_dict.add_comment(f"# origin module:: {self.filename}")
|
502
538
|
self.output_dict.add_docstr(docstr)
|
503
|
-
# Add additional imports to allow one module
|
539
|
+
# Add additional imports to allow one module to refer to another
|
504
540
|
if module_name in MODULE_GLUE.keys():
|
505
541
|
self.output_dict.add_import(MODULE_GLUE[module_name])
|
506
542
|
|
507
543
|
def parse_current_module(self):
|
508
544
|
log.trace(f"# {self.line.rstrip()}")
|
509
|
-
module_name = self.line.split(
|
510
|
-
mod_comment = f"# + module: {self.current_module}.rst"
|
511
|
-
|
545
|
+
# module_name = self.line.split(SEPARATOR)[-1].strip()
|
546
|
+
# mod_comment = f"# + module: {self.current_module}.rst"
|
547
|
+
# now that each .rst is a (sub) module we can process them as such
|
548
|
+
# log.debug(mod_comment)
|
549
|
+
# self.current_module = module_name
|
512
550
|
self.current_function = self.current_class = ""
|
513
|
-
|
514
|
-
self.output_dict.
|
515
|
-
self.output_dict.add_comment(mod_comment)
|
551
|
+
self.output_dict.name = self.current_module
|
552
|
+
# self.output_dict.add_comment(mod_comment)
|
516
553
|
self.line_no += 1 # advance as we did not read any docstring
|
554
|
+
# Add additional imports to allow one module to refer to another
|
555
|
+
if self.current_module in MODULE_GLUE.keys():
|
556
|
+
self.output_dict.add_import(MODULE_GLUE[self.current_module])
|
517
557
|
|
518
558
|
def parse_function(self):
|
519
559
|
log.trace(f"# {self.line.rstrip()}")
|
520
|
-
# this_function = self.line.split(
|
560
|
+
# this_function = self.line.split(SEPARATOR)[-1].strip()
|
521
561
|
# name = this_function
|
522
562
|
|
523
563
|
# Get one or more names
|
@@ -526,7 +566,9 @@ class RSTParser(RSTReader):
|
|
526
566
|
|
527
567
|
for this_function in function_names:
|
528
568
|
# Parse return type from docstring
|
529
|
-
ret_type = return_type_from_context(
|
569
|
+
ret_type = return_type_from_context(
|
570
|
+
docstring=docstr, signature=this_function, module=self.current_module
|
571
|
+
)
|
530
572
|
|
531
573
|
# defaults
|
532
574
|
name = params = ""
|
@@ -548,7 +590,7 @@ class RSTParser(RSTReader):
|
|
548
590
|
# fixup parameters
|
549
591
|
params = self.fix_parameters(params, name)
|
550
592
|
# ASSUME no functions in classes,
|
551
|
-
# so with
|
593
|
+
# so with the cursor at a function this probably means that we are no longer in a class
|
552
594
|
self.leave_class()
|
553
595
|
|
554
596
|
fn_def = FunctionSourceDict(
|
@@ -560,7 +602,7 @@ class RSTParser(RSTReader):
|
|
560
602
|
|
561
603
|
def parse_class(self):
|
562
604
|
log.trace(f"# {self.line.rstrip()}")
|
563
|
-
this_class = self.line.split(
|
605
|
+
this_class = self.line.split(SEPARATOR)[-1].strip() # raw
|
564
606
|
if "(" in this_class:
|
565
607
|
name, params = this_class.split("(", 2)
|
566
608
|
else:
|
@@ -632,7 +674,9 @@ class RSTParser(RSTReader):
|
|
632
674
|
params = self.fix_parameters(params, f"{class_name}.{name}")
|
633
675
|
|
634
676
|
# parse return type from docstring
|
635
|
-
ret_type = return_type_from_context(
|
677
|
+
ret_type = return_type_from_context(
|
678
|
+
docstring=docstr, signature=f"{class_name}.{name}", module=self.current_module
|
679
|
+
)
|
636
680
|
# methods have 4 flavours
|
637
681
|
# - __init__ (self, <params>) -> None:
|
638
682
|
# - classmethod (cls, <params>) -> <ret_type>:
|
@@ -692,7 +736,7 @@ class RSTParser(RSTReader):
|
|
692
736
|
|
693
737
|
def parse_exception(self):
|
694
738
|
log.trace(f"# {self.line.rstrip()}")
|
695
|
-
name = self.line.split(
|
739
|
+
name = self.line.split(SEPARATOR)[1].strip()
|
696
740
|
if name == "Exception":
|
697
741
|
# no need to redefine Exception
|
698
742
|
self.line_no += 1
|
@@ -709,9 +753,9 @@ class RSTParser(RSTReader):
|
|
709
753
|
"get the constant/function/class name from a line with an identifier"
|
710
754
|
# '.. data:: espnow.MAX_DATA_LEN(=250)\n'
|
711
755
|
if line:
|
712
|
-
return line.split(
|
756
|
+
return line.split(SEPARATOR)[-1].strip()
|
713
757
|
else:
|
714
|
-
return self.line.split(
|
758
|
+
return self.line.split(SEPARATOR)[-1].strip()
|
715
759
|
|
716
760
|
def parse_names(self, oneliner: bool = True):
|
717
761
|
"""get a list of constant/function/class names from and following a line with an identifier
|
@@ -734,7 +778,7 @@ class RSTParser(RSTReader):
|
|
734
778
|
log.trace("Sequence detected")
|
735
779
|
names.append(self.parse_name(self.rst_text[self.line_no + counter]))
|
736
780
|
counter += 1
|
737
|
-
# now advance the
|
781
|
+
# now advance the line counter
|
738
782
|
self.line_no += counter - 1
|
739
783
|
# clean up before returning
|
740
784
|
names = [n.strip() for n in names if n.strip() != "etc."] # remove etc.
|
@@ -753,7 +797,9 @@ class RSTParser(RSTReader):
|
|
753
797
|
|
754
798
|
# deal with documentation wildcards
|
755
799
|
for name in names:
|
756
|
-
r_type = return_type_from_context(
|
800
|
+
r_type = return_type_from_context(
|
801
|
+
docstring=docstr, signature=name, module=self.current_module, literal=True
|
802
|
+
)
|
757
803
|
if r_type in ["None"]: # None does not make sense
|
758
804
|
r_type = "Incomplete" # Default to Incomplete/ Unknown / int
|
759
805
|
name = self.strip_prefixes(name)
|
@@ -800,9 +846,10 @@ class RSTWriter(RSTParser):
|
|
800
846
|
def __init__(self, v_tag="v1.xx"):
|
801
847
|
super().__init__(v_tag=v_tag)
|
802
848
|
|
803
|
-
def write_file(self,
|
849
|
+
def write_file(self, target: Path) -> bool:
|
804
850
|
self.prepare_output()
|
805
|
-
|
851
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
852
|
+
return super().write_file(target)
|
806
853
|
|
807
854
|
def prepare_output(self):
|
808
855
|
"Remove trailing spaces and commas from the output."
|
stubber/rst/rst_utils.py
CHANGED
@@ -318,6 +318,7 @@ def distill_return(return_text: str) -> List[Dict]:
|
|
318
318
|
match_string,
|
319
319
|
[
|
320
320
|
"tuple",
|
321
|
+
"a tuple",
|
321
322
|
"a pair",
|
322
323
|
"1-tuple",
|
323
324
|
"2-tuple",
|
@@ -459,7 +460,7 @@ def _type_from_context(
|
|
459
460
|
- then parses the docstring to find references to known types and give then a rating though a hand coded model ()
|
460
461
|
- builds a list return type candidates
|
461
462
|
- selects the highest ranking candidate
|
462
|
-
- the default Type is '
|
463
|
+
- the default Type is 'Incomplete'
|
463
464
|
"""
|
464
465
|
|
465
466
|
if isinstance(docstring, list):
|
@@ -494,19 +495,31 @@ def _type_from_context(
|
|
494
495
|
|
495
496
|
# ------------------------------------------------------
|
496
497
|
# lookup returns that cannot be found based on the docstring from the lookup list
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
498
|
+
if "(" in signature:
|
499
|
+
# method / function / class
|
500
|
+
try:
|
501
|
+
item_name = function_re.findall(signature)[0]
|
502
|
+
except IndexError:
|
503
|
+
item_name = signature.strip().strip(":()")
|
504
|
+
else:
|
505
|
+
# module or class attribute
|
506
|
+
item_name = signature.replace(".. data::", "").strip()
|
507
|
+
|
508
|
+
mod_last = module.split(".")[-1]
|
509
|
+
item_first = item_name.split(".")[0]
|
510
|
+
name_repeat = mod_last == item_first
|
511
|
+
if name_repeat:
|
512
|
+
# avoid machine.UART.UART.irq or pyb.pyb.hid_mouse
|
513
|
+
item_name = f"{module}.{item_name}".replace(f"{mod_last}.{item_first}", mod_last)
|
514
|
+
else:
|
515
|
+
item_name = f"{module}.{item_name}"
|
516
|
+
|
517
|
+
if item_name in LOOKUP_LIST.keys():
|
518
|
+
sig_type = LOOKUP_LIST[item_name][0]
|
506
519
|
return {
|
507
520
|
"type": sig_type,
|
508
521
|
"confidence": C_LOOKUP * WEIGHT_LOOPUPS,
|
509
|
-
"match":
|
522
|
+
"match": item_name,
|
510
523
|
}
|
511
524
|
# ------------------------------------------------------
|
512
525
|
# parse the docstring for simple start verbs,
|
stubber/stubber.py
CHANGED
stubber/stubs_from_docs.py
CHANGED
@@ -20,8 +20,10 @@ def generate_from_rst(
|
|
20
20
|
v_tag: str,
|
21
21
|
release: Optional[str] = None,
|
22
22
|
pattern: str = "*.rst",
|
23
|
-
suffix: str = ".
|
23
|
+
suffix: str = ".pyi",
|
24
24
|
black: bool = True,
|
25
|
+
autoflake: bool = True,
|
26
|
+
clean_rst: bool = True,
|
25
27
|
) -> int:
|
26
28
|
dst_path.mkdir(parents=True, exist_ok=True)
|
27
29
|
if not release:
|
@@ -32,15 +34,15 @@ def generate_from_rst(
|
|
32
34
|
|
33
35
|
files = get_rst_sources(rst_path, pattern)
|
34
36
|
|
35
|
-
#
|
36
|
-
# files = [f for f in files if f.name
|
37
|
+
# reduce files for test/debugging
|
38
|
+
# files = [f for f in files if "machine" in f.name]
|
37
39
|
|
38
40
|
clean_destination(dst_path)
|
39
|
-
make_docstubs(dst_path, v_tag, release, suffix, files)
|
41
|
+
make_docstubs(dst_path, v_tag, release, suffix, files, clean_rst=clean_rst)
|
40
42
|
|
41
43
|
log.info("::group:: start post processing of retrieved stubs")
|
42
44
|
# do not run stubgen
|
43
|
-
utils.do_post_processing([dst_path], stubgen=False, black=black, autoflake=
|
45
|
+
utils.do_post_processing([dst_path], stubgen=False, black=black, autoflake=autoflake)
|
44
46
|
|
45
47
|
# Generate a module manifest for the docstubs
|
46
48
|
utils.make_manifest(
|
@@ -65,26 +67,42 @@ def clean_destination(dst_path: Path):
|
|
65
67
|
|
66
68
|
def get_rst_sources(rst_path: Path, pattern: str) -> List[Path]:
|
67
69
|
"""Get the list of rst files to process"""
|
68
|
-
files = [f for f in rst_path.glob(pattern) if f.stem != "index" and "." not in f.stem]
|
70
|
+
files = [f for f in rst_path.glob(pattern) if f.stem != "index"] # and "." not in f.stem]
|
69
71
|
|
70
72
|
# - excluded modules, ones that offer little advantage or cause much problems
|
71
73
|
files = [f for f in files if f.name not in DOCSTUB_SKIP]
|
72
74
|
return files
|
73
75
|
|
74
76
|
|
75
|
-
def make_docstubs(
|
77
|
+
def make_docstubs(
|
78
|
+
dst_path: Path, v_tag: str, release: str, suffix: str, files: List[Path], clean_rst: bool
|
79
|
+
):
|
76
80
|
"""Create the docstubs"""
|
77
81
|
|
78
82
|
for file in files:
|
79
83
|
reader = RSTWriter(v_tag)
|
84
|
+
reader.clean_rst = clean_rst
|
80
85
|
reader.source_release = release
|
81
86
|
log.debug(f"Reading: {file}")
|
82
87
|
reader.read_file(file)
|
83
88
|
reader.parse()
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
89
|
+
# Destination = "module.__init__.pyi"
|
90
|
+
if "." in file.stem:
|
91
|
+
target = dst_path / f"{(file.stem).replace('.', '/')}{suffix}"
|
92
|
+
else:
|
93
|
+
target = dst_path / file.stem / f"__init__{suffix}"
|
94
|
+
# fname = (dst_path / file.name).with_suffix(suffix)
|
95
|
+
reader.write_file(target)
|
90
96
|
del reader
|
97
|
+
for name in U_MODULES:
|
98
|
+
# create a file "umodule.pyi" for each module
|
99
|
+
# and add a line : from module import *
|
100
|
+
# this is to allow the use of the u modules in the code
|
101
|
+
|
102
|
+
# create the file
|
103
|
+
target = dst_path / f"u{name}.pyi"
|
104
|
+
with open(target, "w") as f:
|
105
|
+
f.write(f"# {name} module\n")
|
106
|
+
f.write("# Allow the use of micro-module notation \n\n")
|
107
|
+
f.write(f"from {name} import * # type: ignore\n")
|
108
|
+
f.flush()
|
stubber/update_module_list.py
CHANGED
@@ -23,7 +23,7 @@ def read_modules(path: Optional[Path] = None) -> Set[str]:
|
|
23
23
|
read text files with modules per firmware.
|
24
24
|
each contains the output of help("modules")
|
25
25
|
- lines starting with # are comments.
|
26
|
-
- split the other lines at whitespace
|
26
|
+
- split the other lines at whitespace separator,
|
27
27
|
- and add each module to a set
|
28
28
|
"""
|
29
29
|
path = Path(path or "./data")
|
@@ -73,7 +73,7 @@ def update_module_list():
|
|
73
73
|
"upip_utarfile",
|
74
74
|
"upysh",
|
75
75
|
"uasyncio",
|
76
|
-
"builtins",
|
76
|
+
# "builtins", # may be used for type hints
|
77
77
|
"re",
|
78
78
|
}
|
79
79
|
log.info("Update the module list in createstubs.py")
|
stubber/utils/config.py
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
"""stubber configuration"""
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import List
|
4
|
+
from typing import List, Optional, Union
|
5
5
|
|
6
6
|
from mpflash.logger import log
|
7
|
+
from mpflash.versions import get_preview_mp_version, get_stable_mp_version, micropython_versions
|
7
8
|
from typedconfig.config import Config, key, section
|
8
9
|
from typedconfig.source import EnvironmentConfigSource
|
9
10
|
|
10
|
-
import mpflash.basicgit as git
|
11
|
-
from mpflash.versions import V_PREVIEW
|
12
|
-
|
13
11
|
from .typed_config_toml import TomlConfigSource
|
14
12
|
|
15
13
|
|
16
14
|
@section("micropython-stubber")
|
17
15
|
class StubberConfig(Config):
|
16
|
+
_config_path = None
|
17
|
+
|
18
18
|
"stubber configuration class"
|
19
19
|
# relative to stubs folder
|
20
20
|
fallback_path: Path = key(
|
@@ -45,9 +45,13 @@ class StubberConfig(Config):
|
|
45
45
|
stable_version: str = key(
|
46
46
|
key_name="stable-version", cast=str, required=False, default="1.20.0"
|
47
47
|
)
|
48
|
-
|
49
48
|
"last published stable"
|
50
49
|
|
50
|
+
preview_version: str = key(
|
51
|
+
key_name="preview-version", cast=str, required=False, default="preview"
|
52
|
+
)
|
53
|
+
"current preview version"
|
54
|
+
|
51
55
|
all_versions = key(
|
52
56
|
key_name="all-versions",
|
53
57
|
cast=list,
|
@@ -79,6 +83,15 @@ class StubberConfig(Config):
|
|
79
83
|
"return the stubs path in the microypthon-stubs repo"
|
80
84
|
return self.mpy_stubs_path / "publish" / "template"
|
81
85
|
|
86
|
+
@property
|
87
|
+
def config_path(self) -> Path:
|
88
|
+
"return the config path"
|
89
|
+
return self._config_path
|
90
|
+
|
91
|
+
@config_path.setter
|
92
|
+
def config_path(self, value: Path):
|
93
|
+
self._config_path = value
|
94
|
+
|
82
95
|
def post_read_hook(self) -> dict:
|
83
96
|
config_updates = {}
|
84
97
|
# relative to stubs
|
@@ -94,32 +107,39 @@ class StubberConfig(Config):
|
|
94
107
|
# read the versions from the git tags
|
95
108
|
all_versions = []
|
96
109
|
try:
|
97
|
-
all_versions =
|
110
|
+
all_versions = micropython_versions(minver="v1.17")
|
98
111
|
except Exception as e:
|
99
112
|
log.warning(f"Could not read micropython versions from git: {e}")
|
100
113
|
all_versions = ["1.19", "1.19.1", "1.20.0", "1.21.0"]
|
101
114
|
config_updates.update(all_versions=all_versions)
|
102
115
|
config_updates.update(
|
103
|
-
stable_version=
|
116
|
+
stable_version=get_stable_mp_version(),
|
117
|
+
preview_version=get_preview_mp_version(),
|
104
118
|
) # second last version - last version is the preview version
|
105
119
|
return config_updates
|
106
120
|
|
107
121
|
|
108
|
-
def readconfig(
|
122
|
+
def readconfig(
|
123
|
+
location: Optional[Path] = None,
|
124
|
+
filename: str = "pyproject.toml",
|
125
|
+
prefix: str = "tool.",
|
126
|
+
must_exist: bool = True,
|
127
|
+
):
|
109
128
|
"read the configuration from the pyproject.toml file"
|
110
129
|
# locate the pyproject.toml file
|
111
|
-
|
130
|
+
config_path = location or Path.cwd()
|
112
131
|
use_toml = True
|
113
|
-
while not (
|
114
|
-
|
115
|
-
if
|
132
|
+
while not (config_path / filename).exists():
|
133
|
+
config_path = config_path.parent
|
134
|
+
if config_path == config_path.parent:
|
116
135
|
log.trace(f"Could not find config file: {filename}")
|
117
136
|
use_toml = False
|
118
137
|
break
|
119
138
|
|
120
|
-
filename = str(
|
139
|
+
filename = str(config_path / filename)
|
121
140
|
|
122
141
|
config = StubberConfig()
|
142
|
+
config.config_path = config_path.absolute()
|
123
143
|
# add provider sources to the config
|
124
144
|
config.add_source(EnvironmentConfigSource())
|
125
145
|
if use_toml:
|