micropython-stubber 1.16.2__py3-none-any.whl → 1.17.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.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/METADATA +2 -1
- {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/RECORD +54 -54
- stubber/__init__.py +1 -1
- stubber/basicgit.py +27 -33
- stubber/board/board_info.csv +137 -103
- stubber/board/createstubs.py +222 -189
- stubber/board/createstubs_db.py +284 -214
- stubber/board/createstubs_db_min.py +286 -265
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_lvgl.py +171 -113
- stubber/board/createstubs_lvgl_min.py +738 -275
- stubber/board/createstubs_lvgl_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +237 -174
- stubber/board/createstubs_mem_min.py +263 -247
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +242 -227
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/fw_info.py +135 -0
- stubber/board/modulelist.txt +1 -2
- stubber/codemod/_partials/__init__.py +1 -1
- stubber/codemod/_partials/db_main.py +90 -72
- stubber/codemod/_partials/modules_reader.py +29 -17
- stubber/codemod/board.py +2 -4
- stubber/codemod/enrich.py +1 -1
- stubber/commands/build_cmd.py +6 -4
- stubber/commands/get_docstubs_cmd.py +6 -11
- stubber/commands/get_frozen_cmd.py +6 -11
- stubber/commands/switch_cmd.py +6 -4
- stubber/data/board_info.csv +134 -101
- stubber/data/board_info.json +1357 -901
- stubber/freeze/freeze_manifest_2.py +2 -1
- stubber/freeze/get_frozen.py +28 -13
- stubber/minify.py +56 -43
- stubber/publish/candidates.py +15 -23
- stubber/publish/defaults.py +2 -2
- stubber/publish/merge_docstubs.py +5 -7
- stubber/publish/missing_class_methods.py +2 -2
- stubber/publish/pathnames.py +2 -2
- stubber/publish/publish.py +2 -1
- stubber/publish/stubpackage.py +20 -40
- stubber/rst/lookup.py +9 -7
- stubber/rst/reader.py +2 -1
- stubber/stubber.py +5 -6
- stubber/update_fallback.py +3 -1
- stubber/update_module_list.py +1 -1
- stubber/utils/__init__.py +1 -1
- stubber/utils/config.py +7 -9
- stubber/utils/post.py +1 -1
- stubber/utils/repos.py +10 -7
- stubber/utils/versions.py +48 -7
- stubber/variants.py +3 -3
- stubber/board/logging.py +0 -99
- {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/entry_points.txt +0 -0
stubber/board/createstubs.py
CHANGED
@@ -4,11 +4,14 @@ Create stubs for (all) modules on a MicroPython board
|
|
4
4
|
# Copyright (c) 2019-2023 Jos Verlinde
|
5
5
|
|
6
6
|
import gc
|
7
|
-
import logging
|
8
7
|
import os
|
9
8
|
import sys
|
9
|
+
from time import sleep
|
10
10
|
|
11
|
-
|
11
|
+
try:
|
12
|
+
from ujson import dumps
|
13
|
+
except:
|
14
|
+
from json import dumps
|
12
15
|
|
13
16
|
try:
|
14
17
|
from machine import reset # type: ignore
|
@@ -20,11 +23,49 @@ try:
|
|
20
23
|
except ImportError:
|
21
24
|
from ucollections import OrderedDict # type: ignore
|
22
25
|
|
23
|
-
__version__ = "v1.
|
26
|
+
__version__ = "v1.17.0"
|
24
27
|
ENOENT = 2
|
25
28
|
_MAX_CLASS_LEVEL = 2 # Max class nesting
|
26
|
-
LIBS = ["
|
27
|
-
|
29
|
+
LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."]
|
30
|
+
|
31
|
+
|
32
|
+
# our own logging module to avoid dependency on and interfering with logging module
|
33
|
+
class logging:
|
34
|
+
# DEBUG = 10
|
35
|
+
INFO = 20
|
36
|
+
WARNING = 30
|
37
|
+
ERROR = 40
|
38
|
+
level = INFO
|
39
|
+
prnt = print
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
def getLogger(name):
|
43
|
+
return logging()
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def basicConfig(cls, level):
|
47
|
+
cls.level = level
|
48
|
+
|
49
|
+
# def debug(self, msg):
|
50
|
+
# if self.level <= logging.DEBUG:
|
51
|
+
# self.prnt("DEBUG :", msg)
|
52
|
+
|
53
|
+
def info(self, msg):
|
54
|
+
if self.level <= logging.INFO:
|
55
|
+
self.prnt("INFO :", msg)
|
56
|
+
|
57
|
+
def warning(self, msg):
|
58
|
+
if self.level <= logging.WARNING:
|
59
|
+
self.prnt("WARN :", msg)
|
60
|
+
|
61
|
+
def error(self, msg):
|
62
|
+
if self.level <= logging.ERROR:
|
63
|
+
self.prnt("ERROR :", msg)
|
64
|
+
|
65
|
+
|
66
|
+
log = logging.getLogger("stubber")
|
67
|
+
logging.basicConfig(level=logging.INFO)
|
68
|
+
# logging.basicConfig(level=logging.DEBUG)
|
28
69
|
|
29
70
|
|
30
71
|
class Stubber:
|
@@ -32,24 +73,21 @@ class Stubber:
|
|
32
73
|
|
33
74
|
def __init__(self, path: str = None, firmware_id: str = None): # type: ignore
|
34
75
|
try:
|
35
|
-
if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103":
|
76
|
+
if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore
|
36
77
|
raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed")
|
37
78
|
except AttributeError:
|
38
79
|
pass
|
39
|
-
self.log = None
|
40
|
-
self.log = logging.getLogger("stubber")
|
41
|
-
self._report = [] # type: list[str]
|
42
80
|
self.info = _info()
|
43
|
-
|
44
|
-
|
81
|
+
log.info("Port: {}".format(self.info["port"]))
|
82
|
+
log.info("Board: {}".format(self.info["board"]))
|
45
83
|
gc.collect()
|
46
84
|
if firmware_id:
|
47
85
|
self._fwid = firmware_id.lower()
|
48
86
|
else:
|
49
87
|
if self.info["family"] == "micropython":
|
50
|
-
self._fwid = "{family}-{
|
88
|
+
self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-")
|
51
89
|
else:
|
52
|
-
self._fwid = "{family}-{
|
90
|
+
self._fwid = "{family}-v{version}-{port}".format(**self.info)
|
53
91
|
self._start_free = gc.mem_free() # type: ignore
|
54
92
|
|
55
93
|
if path:
|
@@ -59,11 +97,11 @@ class Stubber:
|
|
59
97
|
path = get_root()
|
60
98
|
|
61
99
|
self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/")
|
62
|
-
|
100
|
+
# log.debug(self.path)
|
63
101
|
try:
|
64
102
|
ensure_folder(path + "/")
|
65
103
|
except OSError:
|
66
|
-
|
104
|
+
log.error("error creating stub folder {}".format(path))
|
67
105
|
self.problematic = [
|
68
106
|
"upip",
|
69
107
|
"upysh",
|
@@ -82,20 +120,23 @@ class Stubber:
|
|
82
120
|
]
|
83
121
|
# there is no option to discover modules from micropython, list is read from an external file.
|
84
122
|
self.modules = [] # type: list[str]
|
123
|
+
self._json_name = None
|
124
|
+
self._json_first = False
|
85
125
|
|
86
126
|
def get_obj_attributes(self, item_instance: object):
|
87
127
|
"extract information of the objects members and attributes"
|
88
128
|
# name_, repr_(value), type as text, item_instance
|
89
129
|
_result = []
|
90
130
|
_errors = []
|
91
|
-
|
131
|
+
# log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
|
92
132
|
for name in dir(item_instance):
|
93
|
-
if name.startswith("
|
133
|
+
if name.startswith("__") and not name in self.modules:
|
94
134
|
continue
|
95
|
-
|
135
|
+
# log.debug("get attribute {}".format(name))
|
96
136
|
try:
|
97
137
|
val = getattr(item_instance, name)
|
98
138
|
# name , item_repr(value) , type as text, item_instance, order
|
139
|
+
# log.debug("attribute {}:{}".format(name, val))
|
99
140
|
try:
|
100
141
|
type_text = repr(type(val)).split("'")[1]
|
101
142
|
except IndexError:
|
@@ -110,11 +151,7 @@ class Stubber:
|
|
110
151
|
order = 4
|
111
152
|
_result.append((name, repr(val), repr(type(val)), val, order))
|
112
153
|
except AttributeError as e:
|
113
|
-
_errors.append(
|
114
|
-
"Couldn't get attribute '{}' from object '{}', Err: {}".format(
|
115
|
-
name, item_instance, e
|
116
|
-
)
|
117
|
-
)
|
154
|
+
_errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e))
|
118
155
|
except MemoryError as e:
|
119
156
|
print("MemoryError: {}".format(e))
|
120
157
|
sleep(1)
|
@@ -132,21 +169,23 @@ class Stubber:
|
|
132
169
|
|
133
170
|
def create_all_stubs(self):
|
134
171
|
"Create stubs for all configured modules"
|
135
|
-
|
172
|
+
log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid))
|
173
|
+
self.report_start()
|
136
174
|
gc.collect()
|
137
175
|
for module_name in self.modules:
|
138
176
|
self.create_one_stub(module_name)
|
139
|
-
self.
|
177
|
+
self.report_end()
|
178
|
+
log.info("Finally done")
|
140
179
|
|
141
180
|
def create_one_stub(self, module_name: str):
|
142
181
|
if module_name in self.problematic:
|
143
|
-
|
182
|
+
log.warning("Skip module: {:<25} : Known problematic".format(module_name))
|
144
183
|
return False
|
145
184
|
if module_name in self.excluded:
|
146
|
-
|
185
|
+
log.warning("Skip module: {:<25} : Excluded".format(module_name))
|
147
186
|
return False
|
148
187
|
|
149
|
-
file_name = "{}/{}.
|
188
|
+
file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/"))
|
150
189
|
gc.collect()
|
151
190
|
result = False
|
152
191
|
try:
|
@@ -161,10 +200,10 @@ class Stubber:
|
|
161
200
|
|
162
201
|
Args:
|
163
202
|
- module_name (str): name of the module to document. This module will be imported.
|
164
|
-
- file_name (Optional[str]): the 'path/filename.
|
203
|
+
- file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name.
|
165
204
|
"""
|
166
205
|
if file_name is None:
|
167
|
-
fname = module_name.replace(".", "_") + ".
|
206
|
+
fname = module_name.replace(".", "_") + ".pyi"
|
168
207
|
file_name = self.path + "/" + fname
|
169
208
|
else:
|
170
209
|
fname = file_name.split("/")[-1]
|
@@ -178,12 +217,10 @@ class Stubber:
|
|
178
217
|
try:
|
179
218
|
new_module = __import__(module_name, None, None, ("*"))
|
180
219
|
m1 = gc.mem_free() # type: ignore
|
181
|
-
|
182
|
-
"Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)
|
183
|
-
)
|
220
|
+
log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
|
184
221
|
|
185
222
|
except ImportError:
|
186
|
-
|
223
|
+
# log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
|
187
224
|
return False
|
188
225
|
|
189
226
|
# Start a new file
|
@@ -195,40 +232,35 @@ class Stubber:
|
|
195
232
|
module_name, self._fwid, info_, __version__
|
196
233
|
)
|
197
234
|
fp.write(s)
|
198
|
-
fp.write(
|
235
|
+
fp.write(
|
236
|
+
"from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n"
|
237
|
+
)
|
199
238
|
self.write_object_stub(fp, new_module, module_name, "")
|
200
239
|
|
201
|
-
self.
|
202
|
-
'{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))
|
203
|
-
)
|
240
|
+
self.report_add(module_name, file_name)
|
204
241
|
|
205
242
|
if module_name not in {"os", "sys", "logging", "gc"}:
|
206
243
|
# try to unload the module unless we use it
|
207
244
|
try:
|
208
245
|
del new_module
|
209
246
|
except (OSError, KeyError): # lgtm [py/unreachable-statement]
|
210
|
-
|
211
|
-
try
|
212
|
-
del sys.modules[module_name]
|
213
|
-
except KeyError:
|
214
|
-
self.log.debug("could not del sys.modules[{}]".format(module_name))
|
247
|
+
log.warning("could not del new_module")
|
248
|
+
# do not try to delete from sys.modules - most times it does not work anyway
|
215
249
|
gc.collect()
|
216
250
|
return True
|
217
251
|
|
218
|
-
def write_object_stub(
|
219
|
-
self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0
|
220
|
-
):
|
252
|
+
def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0):
|
221
253
|
"Write a module/object stub to an open file. Can be called recursive."
|
222
254
|
gc.collect()
|
223
255
|
if object_expr in self.problematic:
|
224
|
-
|
256
|
+
log.warning("SKIPPING problematic module:{}".format(object_expr))
|
225
257
|
return
|
226
258
|
|
227
|
-
#
|
259
|
+
# # log.debug("DUMP : {}".format(object_expr))
|
228
260
|
items, errors = self.get_obj_attributes(object_expr)
|
229
261
|
|
230
262
|
if errors:
|
231
|
-
|
263
|
+
log.error(errors)
|
232
264
|
|
233
265
|
for item_name, item_repr, item_type_txt, item_instance, _ in items:
|
234
266
|
# name_, repr_(value), type as text, item_instance, order
|
@@ -236,11 +268,16 @@ class Stubber:
|
|
236
268
|
# do not create stubs for these primitives
|
237
269
|
continue
|
238
270
|
if item_name[0].isdigit():
|
239
|
-
|
271
|
+
log.warning("NameError: invalid name {}".format(item_name))
|
240
272
|
continue
|
241
273
|
# Class expansion only on first 3 levels (bit of a hack)
|
242
|
-
if
|
243
|
-
|
274
|
+
if (
|
275
|
+
item_type_txt == "<class 'type'>"
|
276
|
+
and len(indent) <= _MAX_CLASS_LEVEL * 4
|
277
|
+
# and not obj_name.endswith(".Pin")
|
278
|
+
# avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms
|
279
|
+
):
|
280
|
+
# log.debug("{0}class {1}:".format(indent, item_name))
|
244
281
|
superclass = ""
|
245
282
|
is_exception = (
|
246
283
|
item_name.endswith("Exception")
|
@@ -259,11 +296,11 @@ class Stubber:
|
|
259
296
|
if is_exception:
|
260
297
|
s += indent + " ...\n"
|
261
298
|
fp.write(s)
|
262
|
-
|
299
|
+
continue
|
263
300
|
# write classdef
|
264
301
|
fp.write(s)
|
265
302
|
# first write the class literals and methods
|
266
|
-
|
303
|
+
# log.debug("# recursion over class {0}".format(item_name))
|
267
304
|
self.write_object_stub(
|
268
305
|
fp,
|
269
306
|
item_instance,
|
@@ -277,11 +314,7 @@ class Stubber:
|
|
277
314
|
s += indent + " ...\n\n"
|
278
315
|
fp.write(s)
|
279
316
|
elif any(word in item_type_txt for word in ["method", "function", "closure"]):
|
280
|
-
|
281
|
-
"# def {1} function/method/closure, type = '{0}'".format(
|
282
|
-
item_type_txt, item_name
|
283
|
-
)
|
284
|
-
)
|
317
|
+
# log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
|
285
318
|
# module Function or class method
|
286
319
|
# will accept any number of params
|
287
320
|
# return type Any/Incomplete
|
@@ -292,16 +325,14 @@ class Stubber:
|
|
292
325
|
first = "self, "
|
293
326
|
# class method - add function decoration
|
294
327
|
if "bound_method" in item_type_txt or "bound_method" in item_repr:
|
295
|
-
s = "{}@classmethod\n".format(
|
296
|
-
indent
|
297
|
-
) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret)
|
298
|
-
else:
|
299
|
-
s = "{}def {}({}*args, **kwargs) -> {}:\n".format(
|
300
|
-
indent, item_name, first, ret
|
328
|
+
s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(
|
329
|
+
indent, item_name, ret
|
301
330
|
)
|
331
|
+
else:
|
332
|
+
s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret)
|
302
333
|
s += indent + " ...\n\n"
|
303
334
|
fp.write(s)
|
304
|
-
|
335
|
+
# log.debug("\n" + s)
|
305
336
|
elif item_type_txt == "<class 'module'>":
|
306
337
|
# Skip imported modules
|
307
338
|
# fp.write("# import {}\n".format(item_name))
|
@@ -311,38 +342,42 @@ class Stubber:
|
|
311
342
|
t = item_type_txt[8:-2]
|
312
343
|
s = ""
|
313
344
|
|
314
|
-
if t in
|
345
|
+
if t in ("str", "int", "float", "bool", "bytearray", "bytes"):
|
315
346
|
# known type: use actual value
|
316
|
-
s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
|
317
|
-
|
347
|
+
# s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
|
348
|
+
s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t)
|
349
|
+
elif t in ("dict", "list", "tuple"):
|
318
350
|
# dict, list , tuple: use empty value
|
319
351
|
ev = {"dict": "{}", "list": "[]", "tuple": "()"}
|
320
|
-
s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
|
352
|
+
# s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
|
353
|
+
s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t)
|
321
354
|
else:
|
322
355
|
# something else
|
323
|
-
if t
|
324
|
-
# Possibly default others to item_instance object ?
|
356
|
+
if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO"
|
325
357
|
# https://docs.python.org/3/tutorial/classes.html#item_instance-objects
|
358
|
+
# use these types for the attribute
|
359
|
+
if t == "generator":
|
360
|
+
t = "Generator"
|
361
|
+
s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
|
362
|
+
else:
|
363
|
+
# Requires Python 3.6 syntax, which is OK for the stubs/pyi
|
326
364
|
t = "Incomplete"
|
327
|
-
|
328
|
-
s = "{0}{1} : {2} ## {3} = {4}\n".format(
|
329
|
-
indent, item_name, t, item_type_txt, item_repr
|
330
|
-
)
|
365
|
+
s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
|
331
366
|
fp.write(s)
|
332
|
-
|
367
|
+
# log.debug("\n" + s)
|
333
368
|
else:
|
334
369
|
# keep only the name
|
335
|
-
|
370
|
+
# log.debug("# all other, type = '{0}'".format(item_type_txt))
|
336
371
|
fp.write("# all other, type = '{0}'\n".format(item_type_txt))
|
337
372
|
|
338
373
|
fp.write(indent + item_name + " # type: Incomplete\n")
|
339
374
|
|
340
|
-
del items
|
341
|
-
del errors
|
342
|
-
try:
|
343
|
-
|
344
|
-
except (OSError, KeyError, NameError):
|
345
|
-
|
375
|
+
# del items
|
376
|
+
# del errors
|
377
|
+
# try:
|
378
|
+
# del item_name, item_repr, item_type_txt, item_instance # type: ignore
|
379
|
+
# except (OSError, KeyError, NameError):
|
380
|
+
# pass
|
346
381
|
|
347
382
|
@property
|
348
383
|
def flat_fwid(self):
|
@@ -358,7 +393,7 @@ class Stubber:
|
|
358
393
|
"Remove all files from the stub folder"
|
359
394
|
if path is None:
|
360
395
|
path = self.path
|
361
|
-
|
396
|
+
log.info("Clean/remove files in folder: {}".format(path))
|
362
397
|
try:
|
363
398
|
os.stat(path) # TEMP workaround mpremote listdir bug -
|
364
399
|
items = os.listdir(path)
|
@@ -376,45 +411,53 @@ class Stubber:
|
|
376
411
|
except OSError:
|
377
412
|
pass
|
378
413
|
|
379
|
-
def
|
380
|
-
"
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
)
|
386
|
-
f_name = "{}/{}".format(self.path, filename)
|
387
|
-
self.log.info("Report file: {}".format(f_name))
|
414
|
+
def report_start(self, filename: str = "modules.json"):
|
415
|
+
"""Start a report of the modules that have been stubbed
|
416
|
+
"create json with list of exported modules"""
|
417
|
+
self._json_name = "{}/{}".format(self.path, filename)
|
418
|
+
self._json_first = True
|
419
|
+
ensure_folder(self._json_name)
|
420
|
+
log.info("Report file: {}".format(self._json_name))
|
388
421
|
gc.collect()
|
389
422
|
try:
|
390
423
|
# write json by node to reduce memory requirements
|
391
|
-
with open(
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
self.
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
424
|
+
with open(self._json_name, "w") as f:
|
425
|
+
f.write("{")
|
426
|
+
f.write(dumps({"firmware": self.info})[1:-1])
|
427
|
+
f.write(",\n")
|
428
|
+
f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
|
429
|
+
f.write(",\n")
|
430
|
+
f.write('"modules" :[\n')
|
431
|
+
|
432
|
+
except OSError as e:
|
433
|
+
log.error("Failed to create the report.")
|
434
|
+
self._json_name = None
|
435
|
+
raise e
|
436
|
+
|
437
|
+
def report_add(self, module_name: str, stub_file: str):
|
438
|
+
"Add a module to the report"
|
439
|
+
# write json by node to reduce memory requirements
|
440
|
+
if not self._json_name:
|
441
|
+
raise Exception("No report file")
|
442
|
+
try:
|
443
|
+
with open(self._json_name, "a") as f:
|
444
|
+
if not self._json_first:
|
445
|
+
f.write(",\n")
|
446
|
+
else:
|
447
|
+
self._json_first = False
|
448
|
+
line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/"))
|
449
|
+
f.write(line)
|
410
450
|
|
411
|
-
|
412
|
-
|
413
|
-
f.write(",\n")
|
414
|
-
f.write(n)
|
451
|
+
except OSError:
|
452
|
+
log.error("Failed to create the report.")
|
415
453
|
|
416
|
-
def
|
417
|
-
|
454
|
+
def report_end(self):
|
455
|
+
if not self._json_name:
|
456
|
+
raise Exception("No report file")
|
457
|
+
with open(self._json_name, "a") as f:
|
458
|
+
f.write("\n]}")
|
459
|
+
# is used as sucess indicator
|
460
|
+
log.info("Path: {}".format(self.path))
|
418
461
|
|
419
462
|
|
420
463
|
def ensure_folder(path: str):
|
@@ -440,23 +483,32 @@ def ensure_folder(path: str):
|
|
440
483
|
|
441
484
|
|
442
485
|
def _build(s):
|
443
|
-
# extract
|
486
|
+
# extract build from sys.version or os.uname().version if available
|
487
|
+
# sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f'
|
488
|
+
# sys.implementation.version: 'v1.13-103-gb137d064e'
|
444
489
|
if not s:
|
445
490
|
return ""
|
446
|
-
if " on " in s
|
447
|
-
|
448
|
-
|
491
|
+
s = s.split(" on ", 1)[0] if " on " in s else s
|
492
|
+
if s.startswith("v"):
|
493
|
+
if not "-" in s:
|
494
|
+
return ""
|
495
|
+
b = s.split("-")[1]
|
496
|
+
return b
|
497
|
+
if not "-preview" in s:
|
498
|
+
return ""
|
499
|
+
b = s.split("-preview")[1].split(".")[1]
|
500
|
+
return b
|
449
501
|
|
450
502
|
|
451
503
|
def _info(): # type:() -> dict[str, str]
|
452
504
|
info = OrderedDict(
|
453
505
|
{
|
454
|
-
"family": sys.implementation.name,
|
506
|
+
"family": sys.implementation.name, # type: ignore
|
455
507
|
"version": "",
|
456
508
|
"build": "",
|
457
509
|
"ver": "",
|
458
510
|
"port": sys.platform, # port: esp32 / win32 / linux / stm32
|
459
|
-
"board": "
|
511
|
+
"board": "UNKNOWN",
|
460
512
|
"cpu": "",
|
461
513
|
"mpy": "",
|
462
514
|
"arch": "",
|
@@ -470,60 +522,46 @@ def _info(): # type:() -> dict[str, str]
|
|
470
522
|
elif info["port"] == "linux":
|
471
523
|
info["port"] = "unix"
|
472
524
|
try:
|
473
|
-
info["version"] =
|
525
|
+
info["version"] = version_str(sys.implementation.version) # type: ignore
|
474
526
|
except AttributeError:
|
475
527
|
pass
|
476
528
|
try:
|
477
|
-
|
478
|
-
sys.implementation._machine
|
479
|
-
if "_machine" in dir(sys.implementation)
|
480
|
-
else os.uname().machine
|
529
|
+
_machine = (
|
530
|
+
sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore
|
481
531
|
)
|
482
|
-
info["board"] =
|
483
|
-
info["
|
532
|
+
# info["board"] = "with".join(_machine.split("with")[:-1]).strip()
|
533
|
+
info["board"] = _machine
|
534
|
+
info["cpu"] = _machine.split("with")[-1].strip()
|
484
535
|
info["mpy"] = (
|
485
|
-
sys.implementation._mpy
|
536
|
+
sys.implementation._mpy # type: ignore
|
486
537
|
if "_mpy" in dir(sys.implementation)
|
487
|
-
else sys.implementation.mpy
|
538
|
+
else sys.implementation.mpy # type: ignore
|
488
539
|
if "mpy" in dir(sys.implementation)
|
489
540
|
else ""
|
490
541
|
)
|
491
542
|
except (AttributeError, IndexError):
|
492
543
|
pass
|
493
|
-
|
494
|
-
for filename in [d + "/board_info.csv" for d in LIBS]:
|
495
|
-
# print("look up the board name in the file", filename)
|
496
|
-
if file_exists(filename):
|
497
|
-
# print("Found board info file: {}".format(filename))
|
498
|
-
b = info["board"].strip()
|
499
|
-
if find_board(info, b, filename):
|
500
|
-
break
|
501
|
-
if "with" in b:
|
502
|
-
b = b.split("with")[0].strip()
|
503
|
-
if find_board(info, b, filename):
|
504
|
-
break
|
505
|
-
info["board"] = "GENERIC"
|
506
|
-
info["board"] = info["board"].replace(" ", "_")
|
507
|
-
gc.collect()
|
544
|
+
info["board"] = get_boardname()
|
508
545
|
|
509
546
|
try:
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
547
|
+
if "uname" in dir(os): # old
|
548
|
+
# extract build from uname().version if available
|
549
|
+
info["build"] = _build(os.uname()[3]) # type: ignore
|
550
|
+
if not info["build"]:
|
551
|
+
# extract build from uname().release if available
|
552
|
+
info["build"] = _build(os.uname()[2]) # type: ignore
|
553
|
+
elif "version" in dir(sys): # new
|
554
|
+
# extract build from sys.version if available
|
555
|
+
info["build"] = _build(sys.version)
|
556
|
+
except (AttributeError, IndexError, TypeError):
|
519
557
|
pass
|
520
558
|
# avoid build hashes
|
521
|
-
if info["build"] and len(info["build"]) > 5:
|
522
|
-
|
559
|
+
# if info["build"] and len(info["build"]) > 5:
|
560
|
+
# info["build"] = ""
|
523
561
|
|
524
562
|
if info["version"] == "" and sys.platform not in ("unix", "win32"):
|
525
563
|
try:
|
526
|
-
u = os.uname()
|
564
|
+
u = os.uname() # type: ignore
|
527
565
|
info["version"] = u.release
|
528
566
|
except (IndexError, AttributeError, TypeError):
|
529
567
|
pass
|
@@ -545,14 +583,14 @@ def _info(): # type:() -> dict[str, str]
|
|
545
583
|
info["release"] = "2.0.0"
|
546
584
|
|
547
585
|
if info["family"] == "micropython":
|
586
|
+
info["version"]
|
548
587
|
if (
|
549
588
|
info["version"]
|
550
589
|
and info["version"].endswith(".0")
|
551
|
-
and info["version"]
|
552
|
-
>= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0
|
590
|
+
and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0
|
553
591
|
and info["version"] <= "1.19.9"
|
554
592
|
):
|
555
|
-
#
|
593
|
+
# versions from 1.10.0 to 1.20.0 do not have a micro .0
|
556
594
|
info["version"] = info["version"][:-2]
|
557
595
|
|
558
596
|
# spell-checker: disable
|
@@ -576,25 +614,31 @@ def _info(): # type:() -> dict[str, str]
|
|
576
614
|
info["arch"] = arch
|
577
615
|
# .mpy version.minor
|
578
616
|
info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3)
|
617
|
+
if info["build"] and not info["version"].endswith("-preview"):
|
618
|
+
info["version"] = info["version"] + "-preview"
|
579
619
|
# simple to use version[-build] string
|
580
|
-
info["ver"] = f"
|
620
|
+
info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}"
|
581
621
|
|
582
622
|
return info
|
583
623
|
|
584
624
|
|
585
|
-
def
|
586
|
-
"
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
625
|
+
def version_str(version: tuple): # -> str:
|
626
|
+
v_str = ".".join([str(n) for n in version[:3]])
|
627
|
+
if len(version) > 3 and version[3]:
|
628
|
+
v_str += "-" + version[3]
|
629
|
+
return v_str
|
630
|
+
|
631
|
+
|
632
|
+
def get_boardname() -> str:
|
633
|
+
"Read the board name from the boardname.py file that may have been created upfront"
|
634
|
+
try:
|
635
|
+
from boardname import BOARDNAME # type: ignore
|
636
|
+
|
637
|
+
log.info("Found BOARDNAME: {}".format(BOARDNAME))
|
638
|
+
except ImportError:
|
639
|
+
log.warning("BOARDNAME not found")
|
640
|
+
BOARDNAME = ""
|
641
|
+
return BOARDNAME
|
598
642
|
|
599
643
|
|
600
644
|
def get_root() -> str: # sourcery skip: use-assigned-variable
|
@@ -647,10 +691,6 @@ def is_micropython() -> bool:
|
|
647
691
|
# pylint: disable=unused-variable,eval-used
|
648
692
|
try:
|
649
693
|
# either test should fail on micropython
|
650
|
-
# a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces
|
651
|
-
# Micropython : SyntaxError
|
652
|
-
# a = eval("1and 0") # lgtm [py/unused-local-variable]
|
653
|
-
# Eval blocks some minification aspects
|
654
694
|
|
655
695
|
# b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented
|
656
696
|
# Micropython: NotImplementedError
|
@@ -915,16 +955,9 @@ def main():
|
|
915
955
|
gc.collect()
|
916
956
|
|
917
957
|
stubber.create_all_stubs()
|
918
|
-
stubber.report()
|
919
958
|
|
920
959
|
|
921
960
|
if __name__ == "__main__" or is_micropython():
|
922
|
-
try:
|
923
|
-
log = logging.getLogger("stubber")
|
924
|
-
logging.basicConfig(level=logging.INFO)
|
925
|
-
# logging.basicConfig(level=logging.DEBUG)
|
926
|
-
except NameError:
|
927
|
-
pass
|
928
961
|
if not file_exists("no_auto_stubber.txt"):
|
929
962
|
try:
|
930
963
|
gc.threshold(4 * 1024) # type: ignore
|