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.
Files changed (55) hide show
  1. {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/METADATA +2 -1
  2. {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/RECORD +54 -54
  3. stubber/__init__.py +1 -1
  4. stubber/basicgit.py +27 -33
  5. stubber/board/board_info.csv +137 -103
  6. stubber/board/createstubs.py +222 -189
  7. stubber/board/createstubs_db.py +284 -214
  8. stubber/board/createstubs_db_min.py +286 -265
  9. stubber/board/createstubs_db_mpy.mpy +0 -0
  10. stubber/board/createstubs_lvgl.py +171 -113
  11. stubber/board/createstubs_lvgl_min.py +738 -275
  12. stubber/board/createstubs_lvgl_mpy.mpy +0 -0
  13. stubber/board/createstubs_mem.py +237 -174
  14. stubber/board/createstubs_mem_min.py +263 -247
  15. stubber/board/createstubs_mem_mpy.mpy +0 -0
  16. stubber/board/createstubs_min.py +242 -227
  17. stubber/board/createstubs_mpy.mpy +0 -0
  18. stubber/board/fw_info.py +135 -0
  19. stubber/board/modulelist.txt +1 -2
  20. stubber/codemod/_partials/__init__.py +1 -1
  21. stubber/codemod/_partials/db_main.py +90 -72
  22. stubber/codemod/_partials/modules_reader.py +29 -17
  23. stubber/codemod/board.py +2 -4
  24. stubber/codemod/enrich.py +1 -1
  25. stubber/commands/build_cmd.py +6 -4
  26. stubber/commands/get_docstubs_cmd.py +6 -11
  27. stubber/commands/get_frozen_cmd.py +6 -11
  28. stubber/commands/switch_cmd.py +6 -4
  29. stubber/data/board_info.csv +134 -101
  30. stubber/data/board_info.json +1357 -901
  31. stubber/freeze/freeze_manifest_2.py +2 -1
  32. stubber/freeze/get_frozen.py +28 -13
  33. stubber/minify.py +56 -43
  34. stubber/publish/candidates.py +15 -23
  35. stubber/publish/defaults.py +2 -2
  36. stubber/publish/merge_docstubs.py +5 -7
  37. stubber/publish/missing_class_methods.py +2 -2
  38. stubber/publish/pathnames.py +2 -2
  39. stubber/publish/publish.py +2 -1
  40. stubber/publish/stubpackage.py +20 -40
  41. stubber/rst/lookup.py +9 -7
  42. stubber/rst/reader.py +2 -1
  43. stubber/stubber.py +5 -6
  44. stubber/update_fallback.py +3 -1
  45. stubber/update_module_list.py +1 -1
  46. stubber/utils/__init__.py +1 -1
  47. stubber/utils/config.py +7 -9
  48. stubber/utils/post.py +1 -1
  49. stubber/utils/repos.py +10 -7
  50. stubber/utils/versions.py +48 -7
  51. stubber/variants.py +3 -3
  52. stubber/board/logging.py +0 -99
  53. {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/LICENSE +0 -0
  54. {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/WHEEL +0 -0
  55. {micropython_stubber-1.16.2.dist-info → micropython_stubber-1.17.0.dist-info}/entry_points.txt +0 -0
@@ -18,16 +18,19 @@ Create stubs for (all) modules on a MicroPython board.
18
18
  - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device
19
19
 
20
20
 
21
- This variant was generated from createstubs.py by micropython-stubber v1.16.2
21
+ This variant was generated from createstubs.py by micropython-stubber v1.17.0
22
22
  """
23
23
  # Copyright (c) 2019-2023 Jos Verlinde
24
24
 
25
25
  import gc
26
- import logging
27
26
  import os
28
27
  import sys
28
+ from time import sleep
29
29
 
30
- from ujson import dumps
30
+ try:
31
+ from ujson import dumps
32
+ except:
33
+ from json import dumps
31
34
 
32
35
  try:
33
36
  from machine import reset # type: ignore
@@ -39,11 +42,49 @@ try:
39
42
  except ImportError:
40
43
  from ucollections import OrderedDict # type: ignore
41
44
 
42
- __version__ = "v1.16.2"
45
+ __version__ = "v1.17.0"
43
46
  ENOENT = 2
44
47
  _MAX_CLASS_LEVEL = 2 # Max class nesting
45
- LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"]
46
- from time import sleep
48
+ LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."]
49
+
50
+
51
+ # our own logging module to avoid dependency on and interfering with logging module
52
+ class logging:
53
+ # DEBUG = 10
54
+ INFO = 20
55
+ WARNING = 30
56
+ ERROR = 40
57
+ level = INFO
58
+ prnt = print
59
+
60
+ @staticmethod
61
+ def getLogger(name):
62
+ return logging()
63
+
64
+ @classmethod
65
+ def basicConfig(cls, level):
66
+ cls.level = level
67
+
68
+ # def debug(self, msg):
69
+ # if self.level <= logging.DEBUG:
70
+ # self.prnt("DEBUG :", msg)
71
+
72
+ def info(self, msg):
73
+ if self.level <= logging.INFO:
74
+ self.prnt("INFO :", msg)
75
+
76
+ def warning(self, msg):
77
+ if self.level <= logging.WARNING:
78
+ self.prnt("WARN :", msg)
79
+
80
+ def error(self, msg):
81
+ if self.level <= logging.ERROR:
82
+ self.prnt("ERROR :", msg)
83
+
84
+
85
+ log = logging.getLogger("stubber")
86
+ logging.basicConfig(level=logging.INFO)
87
+ # logging.basicConfig(level=logging.DEBUG)
47
88
 
48
89
 
49
90
  class Stubber:
@@ -51,24 +92,21 @@ class Stubber:
51
92
 
52
93
  def __init__(self, path: str = None, firmware_id: str = None): # type: ignore
53
94
  try:
54
- if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103":
95
+ if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore
55
96
  raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed")
56
97
  except AttributeError:
57
98
  pass
58
- self.log = None
59
- self.log = logging.getLogger("stubber")
60
- self._report = [] # type: list[str]
61
99
  self.info = _info()
62
- self.log.info("Port: {}".format(self.info["port"]))
63
- self.log.info("Board: {}".format(self.info["board"]))
100
+ log.info("Port: {}".format(self.info["port"]))
101
+ log.info("Board: {}".format(self.info["board"]))
64
102
  gc.collect()
65
103
  if firmware_id:
66
104
  self._fwid = firmware_id.lower()
67
105
  else:
68
106
  if self.info["family"] == "micropython":
69
- self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info)
107
+ self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-")
70
108
  else:
71
- self._fwid = "{family}-{ver}-{port}".format(**self.info)
109
+ self._fwid = "{family}-v{version}-{port}".format(**self.info)
72
110
  self._start_free = gc.mem_free() # type: ignore
73
111
 
74
112
  if path:
@@ -78,11 +116,11 @@ class Stubber:
78
116
  path = get_root()
79
117
 
80
118
  self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/")
81
- self.log.debug(self.path)
119
+ # log.debug(self.path)
82
120
  try:
83
121
  ensure_folder(path + "/")
84
122
  except OSError:
85
- self.log.error("error creating stub folder {}".format(path))
123
+ log.error("error creating stub folder {}".format(path))
86
124
  self.problematic = [
87
125
  "upip",
88
126
  "upysh",
@@ -101,20 +139,23 @@ class Stubber:
101
139
  ]
102
140
  # there is no option to discover modules from micropython, list is read from an external file.
103
141
  self.modules = [] # type: list[str]
142
+ self._json_name = None
143
+ self._json_first = False
104
144
 
105
145
  def get_obj_attributes(self, item_instance: object):
106
146
  "extract information of the objects members and attributes"
107
147
  # name_, repr_(value), type as text, item_instance
108
148
  _result = []
109
149
  _errors = []
110
- self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
150
+ # log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
111
151
  for name in dir(item_instance):
112
- if name.startswith("_") and not name in self.modules:
152
+ if name.startswith("__") and not name in self.modules:
113
153
  continue
114
- self.log.debug("get attribute {}".format(name))
154
+ # log.debug("get attribute {}".format(name))
115
155
  try:
116
156
  val = getattr(item_instance, name)
117
157
  # name , item_repr(value) , type as text, item_instance, order
158
+ # log.debug("attribute {}:{}".format(name, val))
118
159
  try:
119
160
  type_text = repr(type(val)).split("'")[1]
120
161
  except IndexError:
@@ -147,21 +188,23 @@ class Stubber:
147
188
 
148
189
  def create_all_stubs(self):
149
190
  "Create stubs for all configured modules"
150
- self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid))
191
+ log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid))
192
+ self.report_start()
151
193
  gc.collect()
152
194
  for module_name in self.modules:
153
195
  self.create_one_stub(module_name)
154
- self.log.info("Finally done")
196
+ self.report_end()
197
+ log.info("Finally done")
155
198
 
156
199
  def create_one_stub(self, module_name: str):
157
200
  if module_name in self.problematic:
158
- self.log.warning("Skip module: {:<25} : Known problematic".format(module_name))
201
+ log.warning("Skip module: {:<25} : Known problematic".format(module_name))
159
202
  return False
160
203
  if module_name in self.excluded:
161
- self.log.warning("Skip module: {:<25} : Excluded".format(module_name))
204
+ log.warning("Skip module: {:<25} : Excluded".format(module_name))
162
205
  return False
163
206
 
164
- file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/"))
207
+ file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/"))
165
208
  gc.collect()
166
209
  result = False
167
210
  try:
@@ -176,10 +219,10 @@ class Stubber:
176
219
 
177
220
  Args:
178
221
  - module_name (str): name of the module to document. This module will be imported.
179
- - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name.
222
+ - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name.
180
223
  """
181
224
  if file_name is None:
182
- fname = module_name.replace(".", "_") + ".py"
225
+ fname = module_name.replace(".", "_") + ".pyi"
183
226
  file_name = self.path + "/" + fname
184
227
  else:
185
228
  fname = file_name.split("/")[-1]
@@ -193,10 +236,10 @@ class Stubber:
193
236
  try:
194
237
  new_module = __import__(module_name, None, None, ("*"))
195
238
  m1 = gc.mem_free() # type: ignore
196
- self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
239
+ log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
197
240
 
198
241
  except ImportError:
199
- self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
242
+ # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
200
243
  return False
201
244
 
202
245
  # Start a new file
@@ -206,21 +249,18 @@ class Stubber:
206
249
  info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}")
207
250
  s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__)
208
251
  fp.write(s)
209
- fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n")
252
+ fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n")
210
253
  self.write_object_stub(fp, new_module, module_name, "")
211
254
 
212
- self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/")))
255
+ self.report_add(module_name, file_name)
213
256
 
214
257
  if module_name not in {"os", "sys", "logging", "gc"}:
215
258
  # try to unload the module unless we use it
216
259
  try:
217
260
  del new_module
218
261
  except (OSError, KeyError): # lgtm [py/unreachable-statement]
219
- self.log.warning("could not del new_module")
220
- try:
221
- del sys.modules[module_name]
222
- except KeyError:
223
- self.log.debug("could not del sys.modules[{}]".format(module_name))
262
+ log.warning("could not del new_module")
263
+ # do not try to delete from sys.modules - most times it does not work anyway
224
264
  gc.collect()
225
265
  return True
226
266
 
@@ -228,14 +268,14 @@ class Stubber:
228
268
  "Write a module/object stub to an open file. Can be called recursive."
229
269
  gc.collect()
230
270
  if object_expr in self.problematic:
231
- self.log.warning("SKIPPING problematic module:{}".format(object_expr))
271
+ log.warning("SKIPPING problematic module:{}".format(object_expr))
232
272
  return
233
273
 
234
- # self.log.debug("DUMP : {}".format(object_expr))
274
+ # # log.debug("DUMP : {}".format(object_expr))
235
275
  items, errors = self.get_obj_attributes(object_expr)
236
276
 
237
277
  if errors:
238
- self.log.error(errors)
278
+ log.error(errors)
239
279
 
240
280
  for item_name, item_repr, item_type_txt, item_instance, _ in items:
241
281
  # name_, repr_(value), type as text, item_instance, order
@@ -243,11 +283,16 @@ class Stubber:
243
283
  # do not create stubs for these primitives
244
284
  continue
245
285
  if item_name[0].isdigit():
246
- self.log.warning("NameError: invalid name {}".format(item_name))
286
+ log.warning("NameError: invalid name {}".format(item_name))
247
287
  continue
248
288
  # Class expansion only on first 3 levels (bit of a hack)
249
- if item_type_txt == "<class 'type'>" and len(indent) <= _MAX_CLASS_LEVEL * 4:
250
- self.log.debug("{0}class {1}:".format(indent, item_name))
289
+ if (
290
+ item_type_txt == "<class 'type'>"
291
+ and len(indent) <= _MAX_CLASS_LEVEL * 4
292
+ # and not obj_name.endswith(".Pin")
293
+ # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms
294
+ ):
295
+ # log.debug("{0}class {1}:".format(indent, item_name))
251
296
  superclass = ""
252
297
  is_exception = (
253
298
  item_name.endswith("Exception")
@@ -266,11 +311,11 @@ class Stubber:
266
311
  if is_exception:
267
312
  s += indent + " ...\n"
268
313
  fp.write(s)
269
- return
314
+ continue
270
315
  # write classdef
271
316
  fp.write(s)
272
317
  # first write the class literals and methods
273
- self.log.debug("# recursion over class {0}".format(item_name))
318
+ # log.debug("# recursion over class {0}".format(item_name))
274
319
  self.write_object_stub(
275
320
  fp,
276
321
  item_instance,
@@ -284,7 +329,7 @@ class Stubber:
284
329
  s += indent + " ...\n\n"
285
330
  fp.write(s)
286
331
  elif any(word in item_type_txt for word in ["method", "function", "closure"]):
287
- self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
332
+ # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
288
333
  # module Function or class method
289
334
  # will accept any number of params
290
335
  # return type Any/Incomplete
@@ -300,7 +345,7 @@ class Stubber:
300
345
  s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret)
301
346
  s += indent + " ...\n\n"
302
347
  fp.write(s)
303
- self.log.debug("\n" + s)
348
+ # log.debug("\n" + s)
304
349
  elif item_type_txt == "<class 'module'>":
305
350
  # Skip imported modules
306
351
  # fp.write("# import {}\n".format(item_name))
@@ -310,36 +355,42 @@ class Stubber:
310
355
  t = item_type_txt[8:-2]
311
356
  s = ""
312
357
 
313
- if t in ["str", "int", "float", "bool", "bytearray", "bytes"]:
358
+ if t in ("str", "int", "float", "bool", "bytearray", "bytes"):
314
359
  # known type: use actual value
315
- s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
316
- elif t in ["dict", "list", "tuple"]:
360
+ # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
361
+ s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t)
362
+ elif t in ("dict", "list", "tuple"):
317
363
  # dict, list , tuple: use empty value
318
364
  ev = {"dict": "{}", "list": "[]", "tuple": "()"}
319
- s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
365
+ # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
366
+ s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t)
320
367
  else:
321
368
  # something else
322
- if t not in ["object", "set", "frozenset"]:
323
- # Possibly default others to item_instance object ?
369
+ if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO"
324
370
  # https://docs.python.org/3/tutorial/classes.html#item_instance-objects
371
+ # use these types for the attribute
372
+ if t == "generator":
373
+ t = "Generator"
374
+ s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
375
+ else:
376
+ # Requires Python 3.6 syntax, which is OK for the stubs/pyi
325
377
  t = "Incomplete"
326
- # Requires Python 3.6 syntax, which is OK for the stubs/pyi
327
- s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
378
+ s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
328
379
  fp.write(s)
329
- self.log.debug("\n" + s)
380
+ # log.debug("\n" + s)
330
381
  else:
331
382
  # keep only the name
332
- self.log.debug("# all other, type = '{0}'".format(item_type_txt))
383
+ # log.debug("# all other, type = '{0}'".format(item_type_txt))
333
384
  fp.write("# all other, type = '{0}'\n".format(item_type_txt))
334
385
 
335
386
  fp.write(indent + item_name + " # type: Incomplete\n")
336
387
 
337
- del items
338
- del errors
339
- try:
340
- del item_name, item_repr, item_type_txt, item_instance # type: ignore
341
- except (OSError, KeyError, NameError):
342
- pass
388
+ # del items
389
+ # del errors
390
+ # try:
391
+ # del item_name, item_repr, item_type_txt, item_instance # type: ignore
392
+ # except (OSError, KeyError, NameError):
393
+ # pass
343
394
 
344
395
  @property
345
396
  def flat_fwid(self):
@@ -355,7 +406,7 @@ class Stubber:
355
406
  "Remove all files from the stub folder"
356
407
  if path is None:
357
408
  path = self.path
358
- self.log.info("Clean/remove files in folder: {}".format(path))
409
+ log.info("Clean/remove files in folder: {}".format(path))
359
410
  try:
360
411
  os.stat(path) # TEMP workaround mpremote listdir bug -
361
412
  items = os.listdir(path)
@@ -373,41 +424,53 @@ class Stubber:
373
424
  except OSError:
374
425
  pass
375
426
 
376
- def report(self, filename: str = "modules.json"):
377
- "create json with list of exported modules"
378
- self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path))
379
- f_name = "{}/{}".format(self.path, filename)
380
- self.log.info("Report file: {}".format(f_name))
427
+ def report_start(self, filename: str = "modules.json"):
428
+ """Start a report of the modules that have been stubbed
429
+ "create json with list of exported modules"""
430
+ self._json_name = "{}/{}".format(self.path, filename)
431
+ self._json_first = True
432
+ ensure_folder(self._json_name)
433
+ log.info("Report file: {}".format(self._json_name))
381
434
  gc.collect()
382
435
  try:
383
436
  # write json by node to reduce memory requirements
384
- with open(f_name, "w") as f:
385
- self.write_json_header(f)
386
- first = True
387
- for n in self._report:
388
- self.write_json_node(f, n, first)
389
- first = False
390
- self.write_json_end(f)
391
- used = self._start_free - gc.mem_free() # type: ignore
392
- self.log.info("Memory used: {0} Kb".format(used // 1024))
393
- except OSError:
394
- self.log.error("Failed to create the report.")
395
-
396
- def write_json_header(self, f):
397
- f.write("{")
398
- f.write(dumps({"firmware": self.info})[1:-1])
399
- f.write(",\n")
400
- f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
401
- f.write(",\n")
402
- f.write('"modules" :[\n')
437
+ with open(self._json_name, "w") as f:
438
+ f.write("{")
439
+ f.write(dumps({"firmware": self.info})[1:-1])
440
+ f.write(",\n")
441
+ f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
442
+ f.write(",\n")
443
+ f.write('"modules" :[\n')
444
+
445
+ except OSError as e:
446
+ log.error("Failed to create the report.")
447
+ self._json_name = None
448
+ raise e
449
+
450
+ def report_add(self, module_name: str, stub_file: str):
451
+ "Add a module to the report"
452
+ # write json by node to reduce memory requirements
453
+ if not self._json_name:
454
+ raise Exception("No report file")
455
+ try:
456
+ with open(self._json_name, "a") as f:
457
+ if not self._json_first:
458
+ f.write(",\n")
459
+ else:
460
+ self._json_first = False
461
+ line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/"))
462
+ f.write(line)
403
463
 
404
- def write_json_node(self, f, n, first):
405
- if not first:
406
- f.write(",\n")
407
- f.write(n)
464
+ except OSError:
465
+ log.error("Failed to create the report.")
408
466
 
409
- def write_json_end(self, f):
410
- f.write("\n]}")
467
+ def report_end(self):
468
+ if not self._json_name:
469
+ raise Exception("No report file")
470
+ with open(self._json_name, "a") as f:
471
+ f.write("\n]}")
472
+ # is used as sucess indicator
473
+ log.info("Path: {}".format(self.path))
411
474
 
412
475
 
413
476
  def ensure_folder(path: str):
@@ -433,23 +496,32 @@ def ensure_folder(path: str):
433
496
 
434
497
 
435
498
  def _build(s):
436
- # extract a build nr from a string
499
+ # extract build from sys.version or os.uname().version if available
500
+ # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f'
501
+ # sys.implementation.version: 'v1.13-103-gb137d064e'
437
502
  if not s:
438
503
  return ""
439
- if " on " in s:
440
- s = s.split(" on ", 1)[0]
441
- return s.split("-")[1] if "-" in s else ""
504
+ s = s.split(" on ", 1)[0] if " on " in s else s
505
+ if s.startswith("v"):
506
+ if not "-" in s:
507
+ return ""
508
+ b = s.split("-")[1]
509
+ return b
510
+ if not "-preview" in s:
511
+ return ""
512
+ b = s.split("-preview")[1].split(".")[1]
513
+ return b
442
514
 
443
515
 
444
516
  def _info(): # type:() -> dict[str, str]
445
517
  info = OrderedDict(
446
518
  {
447
- "family": sys.implementation.name,
519
+ "family": sys.implementation.name, # type: ignore
448
520
  "version": "",
449
521
  "build": "",
450
522
  "ver": "",
451
523
  "port": sys.platform, # port: esp32 / win32 / linux / stm32
452
- "board": "GENERIC",
524
+ "board": "UNKNOWN",
453
525
  "cpu": "",
454
526
  "mpy": "",
455
527
  "arch": "",
@@ -463,56 +535,44 @@ def _info(): # type:() -> dict[str, str]
463
535
  elif info["port"] == "linux":
464
536
  info["port"] = "unix"
465
537
  try:
466
- info["version"] = ".".join([str(n) for n in sys.implementation.version])
538
+ info["version"] = version_str(sys.implementation.version) # type: ignore
467
539
  except AttributeError:
468
540
  pass
469
541
  try:
470
- machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine
471
- info["board"] = machine.strip()
472
- info["cpu"] = machine.split("with")[1].strip()
542
+ _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore
543
+ # info["board"] = "with".join(_machine.split("with")[:-1]).strip()
544
+ info["board"] = _machine
545
+ info["cpu"] = _machine.split("with")[-1].strip()
473
546
  info["mpy"] = (
474
- sys.implementation._mpy
547
+ sys.implementation._mpy # type: ignore
475
548
  if "_mpy" in dir(sys.implementation)
476
- else sys.implementation.mpy
549
+ else sys.implementation.mpy # type: ignore
477
550
  if "mpy" in dir(sys.implementation)
478
551
  else ""
479
552
  )
480
553
  except (AttributeError, IndexError):
481
554
  pass
482
- gc.collect()
483
- for filename in [d + "/board_info.csv" for d in LIBS]:
484
- # print("look up the board name in the file", filename)
485
- if file_exists(filename):
486
- # print("Found board info file: {}".format(filename))
487
- b = info["board"].strip()
488
- if find_board(info, b, filename):
489
- break
490
- if "with" in b:
491
- b = b.split("with")[0].strip()
492
- if find_board(info, b, filename):
493
- break
494
- info["board"] = "GENERIC"
495
- info["board"] = info["board"].replace(" ", "_")
496
- gc.collect()
555
+ info["board"] = get_boardname()
497
556
 
498
557
  try:
499
- # extract build from uname().version if available
500
- info["build"] = _build(os.uname()[3])
501
- if not info["build"]:
502
- # extract build from uname().release if available
503
- info["build"] = _build(os.uname()[2])
504
- if not info["build"] and ";" in sys.version:
505
- # extract build from uname().release if available
506
- info["build"] = _build(sys.version.split(";")[1])
507
- except (AttributeError, IndexError):
558
+ if "uname" in dir(os): # old
559
+ # extract build from uname().version if available
560
+ info["build"] = _build(os.uname()[3]) # type: ignore
561
+ if not info["build"]:
562
+ # extract build from uname().release if available
563
+ info["build"] = _build(os.uname()[2]) # type: ignore
564
+ elif "version" in dir(sys): # new
565
+ # extract build from sys.version if available
566
+ info["build"] = _build(sys.version)
567
+ except (AttributeError, IndexError, TypeError):
508
568
  pass
509
569
  # avoid build hashes
510
- if info["build"] and len(info["build"]) > 5:
511
- info["build"] = ""
570
+ # if info["build"] and len(info["build"]) > 5:
571
+ # info["build"] = ""
512
572
 
513
573
  if info["version"] == "" and sys.platform not in ("unix", "win32"):
514
574
  try:
515
- u = os.uname()
575
+ u = os.uname() # type: ignore
516
576
  info["version"] = u.release
517
577
  except (IndexError, AttributeError, TypeError):
518
578
  pass
@@ -534,13 +594,14 @@ def _info(): # type:() -> dict[str, str]
534
594
  info["release"] = "2.0.0"
535
595
 
536
596
  if info["family"] == "micropython":
597
+ info["version"]
537
598
  if (
538
599
  info["version"]
539
600
  and info["version"].endswith(".0")
540
601
  and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0
541
602
  and info["version"] <= "1.19.9"
542
603
  ):
543
- # drop the .0 for newer releases
604
+ # versions from 1.10.0 to 1.20.0 do not have a micro .0
544
605
  info["version"] = info["version"][:-2]
545
606
 
546
607
  # spell-checker: disable
@@ -564,25 +625,31 @@ def _info(): # type:() -> dict[str, str]
564
625
  info["arch"] = arch
565
626
  # .mpy version.minor
566
627
  info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3)
628
+ if info["build"] and not info["version"].endswith("-preview"):
629
+ info["version"] = info["version"] + "-preview"
567
630
  # simple to use version[-build] string
568
- info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}"
631
+ info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}"
569
632
 
570
633
  return info
571
634
 
572
635
 
573
- def find_board(info: dict, board_descr: str, filename: str):
574
- "Find the board in the provided board_info.csv file"
575
- with open(filename, "r") as file:
576
- # ugly code to make testable in python and micropython
577
- while 1:
578
- line = file.readline()
579
- if not line:
580
- break
581
- descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip()
582
- if descr_ == board_descr:
583
- info["board"] = board_
584
- return True
585
- return False
636
+ def version_str(version: tuple): # -> str:
637
+ v_str = ".".join([str(n) for n in version[:3]])
638
+ if len(version) > 3 and version[3]:
639
+ v_str += "-" + version[3]
640
+ return v_str
641
+
642
+
643
+ def get_boardname() -> str:
644
+ "Read the board name from the boardname.py file that may have been created upfront"
645
+ try:
646
+ from boardname import BOARDNAME # type: ignore
647
+
648
+ log.info("Found BOARDNAME: {}".format(BOARDNAME))
649
+ except ImportError:
650
+ log.warning("BOARDNAME not found")
651
+ BOARDNAME = ""
652
+ return BOARDNAME
586
653
 
587
654
 
588
655
  def get_root() -> str: # sourcery skip: use-assigned-variable
@@ -635,10 +702,6 @@ def is_micropython() -> bool:
635
702
  # pylint: disable=unused-variable,eval-used
636
703
  try:
637
704
  # either test should fail on micropython
638
- # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces
639
- # Micropython : SyntaxError
640
- # a = eval("1and 0") # lgtm [py/unused-local-variable]
641
- # Eval blocks some minification aspects
642
705
 
643
706
  # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented
644
707
  # Micropython: NotImplementedError
@@ -652,92 +715,99 @@ def is_micropython() -> bool:
652
715
  return True
653
716
 
654
717
 
655
- def main():
656
- import machine # type: ignore
718
+ SKIP_FILE = "modulelist.done"
719
+
657
720
 
721
+ def get_modules(skip=0):
722
+ # new
723
+ for p in LIBS:
724
+ fname = p + "/modulelist.txt"
725
+ if not file_exists(fname):
726
+ continue
727
+ try:
728
+ with open(fname) as f:
729
+ i = 0
730
+ while True:
731
+ line = f.readline().strip()
732
+ if not line:
733
+ break
734
+ if len(line) > 0 and line[0] == "#":
735
+ continue
736
+ i += 1
737
+ if i < skip:
738
+ continue
739
+ yield line
740
+ break
741
+ except OSError:
742
+ pass
743
+
744
+
745
+ def write_skip(done):
746
+ # write count of modules already processed to file
747
+ with open(SKIP_FILE, "w") as f:
748
+ f.write(str(done) + "\n")
749
+
750
+
751
+ def read_skip():
752
+ # read count of modules already processed from file
753
+ done = 0
658
754
  try:
659
- f = open("modulelist.done", "r+b")
660
- was_running = True
661
- print("Opened existing db")
755
+ with open(SKIP_FILE) as f:
756
+ done = int(f.readline().strip())
662
757
  except OSError:
663
- f = open("modulelist.done", "w+b")
664
- print("created new db")
665
- was_running = False
758
+ pass
759
+ return done
760
+
761
+
762
+ def main():
763
+ import machine # type: ignore
764
+
765
+ was_running = file_exists(SKIP_FILE)
766
+ if was_running:
767
+ log.info("Continue from last run")
768
+ else:
769
+ log.info("Starting new run")
770
+ # try:
771
+ # f = open("modulelist.done", "r+b")
772
+ # was_running = True
773
+ # print("Continue from last run")
774
+ # except OSError:
775
+ # f = open("modulelist.done", "w+b")
776
+ # was_running = False
666
777
  stubber = Stubber(path=read_path())
667
778
 
668
779
  # f_name = "{}/{}".format(stubber.path, "modules.json")
780
+ skip = 0
669
781
  if not was_running:
670
782
  # Only clean folder if this is a first run
671
783
  stubber.clean()
672
- # get list of modules to process
673
- get_modulelist(stubber)
674
- # remove the ones that are already done
675
- modules_done = {} # type: dict[str, str]
676
- try:
677
- with open("modulelist.done") as f:
678
- # not optimal , but works on mpremote and esp8266
679
- for line in f.read().split("\n"):
680
- line = line.strip()
681
- gc.collect()
682
- if len(line) > 0:
683
- key, value = line.split("=", 1)
684
- modules_done[key] = value
685
- except (OSError, SyntaxError):
686
- pass
687
- gc.collect()
688
- # see if we can continue from where we left off
689
- modules = [m for m in stubber.modules if m not in modules_done.keys()]
690
- gc.collect()
691
- for modulename in modules:
784
+ stubber.report_start("modules.json")
785
+ else:
786
+ skip = read_skip()
787
+ stubber._json_name = "{}/{}".format(stubber.path, "modules.json")
788
+
789
+ for modulename in get_modules(skip):
692
790
  # ------------------------------------
693
791
  # do epic shit
694
792
  # but sometimes things fail / run out of memory and reboot
695
- ok = False
696
793
  try:
697
- ok = stubber.create_one_stub(modulename)
794
+ stubber.create_one_stub(modulename)
698
795
  except MemoryError:
699
796
  # RESET AND HOPE THAT IN THE NEXT CYCLE WE PROGRESS FURTHER
700
797
  machine.reset()
701
798
  # -------------------------------------
702
799
  gc.collect()
703
- modules_done[modulename] = str(stubber._report[-1] if ok else "failed")
704
- with open("modulelist.done", "a") as f:
705
- f.write("{}={}\n".format(modulename, "ok" if ok else "failed"))
800
+ # modules_done[modulename] = str(stubber._report[-1] if ok else "failed")
801
+ # with open("modulelist.done", "a") as f:
802
+ # f.write("{}={}\n".format(modulename, "ok" if ok else "failed"))
803
+ skip += 1
804
+ write_skip(skip)
706
805
 
707
- # Finished processing - load all the results , and remove the failed ones
708
- if modules_done:
709
- # stubber.write_json_end(mod_fp)
710
- stubber._report = [v for _, v in modules_done.items() if v != "failed"]
711
- stubber.report()
712
-
713
-
714
- def get_modulelist(stubber):
715
- stubber.modules = [] # avoid duplicates
716
- for p in LIBS:
717
- try:
718
- with open(p + "/modulelist.txt") as f:
719
- print("DEBUG: list of modules: " + p + "/modulelist.txt")
720
- for line in f.read().split("\n"):
721
- line = line.strip()
722
- if len(line) > 0 and line[0] != "#":
723
- stubber.modules.append(line)
724
- gc.collect()
725
- break
726
- except OSError:
727
- pass
728
- if not stubber.modules:
729
- stubber.modules = ["micropython"]
730
- _log.warn("Could not find modulelist.txt, using default modules")
731
- gc.collect()
806
+ print("All modules have been processed, Finalizing report")
807
+ stubber.report_end()
732
808
 
733
809
 
734
810
  if __name__ == "__main__" or is_micropython():
735
- try:
736
- log = logging.getLogger("stubber")
737
- logging.basicConfig(level=logging.INFO)
738
- # logging.basicConfig(level=logging.DEBUG)
739
- except NameError:
740
- pass
741
811
  if not file_exists("no_auto_stubber.txt"):
742
812
  try:
743
813
  gc.threshold(4 * 1024) # type: ignore