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
@@ -9,16 +9,19 @@
9
9
  - cross compilation, using mpy-cross,
10
10
  to avoid the compilation step on the micropython device
11
11
 
12
- This variant was generated from createstubs.py by micropython-stubber v1.16.2
12
+ This variant was generated from createstubs.py by micropython-stubber v1.17.0
13
13
  """
14
14
  # Copyright (c) 2019-2023 Jos Verlinde
15
15
 
16
16
  import gc
17
- import logging
18
17
  import os
19
18
  import sys
19
+ from time import sleep
20
20
 
21
- from ujson import dumps
21
+ try:
22
+ from ujson import dumps
23
+ except:
24
+ from json import dumps
22
25
 
23
26
  try:
24
27
  from machine import reset # type: ignore
@@ -30,11 +33,49 @@ try:
30
33
  except ImportError:
31
34
  from ucollections import OrderedDict # type: ignore
32
35
 
33
- __version__ = "v1.16.2"
36
+ __version__ = "v1.17.0"
34
37
  ENOENT = 2
35
38
  _MAX_CLASS_LEVEL = 2 # Max class nesting
36
- LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"]
37
- from time import sleep
39
+ LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."]
40
+
41
+
42
+ # our own logging module to avoid dependency on and interfering with logging module
43
+ class logging:
44
+ # DEBUG = 10
45
+ INFO = 20
46
+ WARNING = 30
47
+ ERROR = 40
48
+ level = INFO
49
+ prnt = print
50
+
51
+ @staticmethod
52
+ def getLogger(name):
53
+ return logging()
54
+
55
+ @classmethod
56
+ def basicConfig(cls, level):
57
+ cls.level = level
58
+
59
+ # def debug(self, msg):
60
+ # if self.level <= logging.DEBUG:
61
+ # self.prnt("DEBUG :", msg)
62
+
63
+ def info(self, msg):
64
+ if self.level <= logging.INFO:
65
+ self.prnt("INFO :", msg)
66
+
67
+ def warning(self, msg):
68
+ if self.level <= logging.WARNING:
69
+ self.prnt("WARN :", msg)
70
+
71
+ def error(self, msg):
72
+ if self.level <= logging.ERROR:
73
+ self.prnt("ERROR :", msg)
74
+
75
+
76
+ log = logging.getLogger("stubber")
77
+ logging.basicConfig(level=logging.INFO)
78
+ # logging.basicConfig(level=logging.DEBUG)
38
79
 
39
80
 
40
81
  class Stubber:
@@ -42,24 +83,21 @@ class Stubber:
42
83
 
43
84
  def __init__(self, path: str = None, firmware_id: str = None): # type: ignore
44
85
  try:
45
- if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103":
86
+ if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore
46
87
  raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed")
47
88
  except AttributeError:
48
89
  pass
49
- self.log = None
50
- self.log = logging.getLogger("stubber")
51
- self._report = [] # type: list[str]
52
90
  self.info = _info()
53
- self.log.info("Port: {}".format(self.info["port"]))
54
- self.log.info("Board: {}".format(self.info["board"]))
91
+ log.info("Port: {}".format(self.info["port"]))
92
+ log.info("Board: {}".format(self.info["board"]))
55
93
  gc.collect()
56
94
  if firmware_id:
57
95
  self._fwid = firmware_id.lower()
58
96
  else:
59
97
  if self.info["family"] == "micropython":
60
- self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info)
98
+ self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-")
61
99
  else:
62
- self._fwid = "{family}-{ver}-{port}".format(**self.info)
100
+ self._fwid = "{family}-v{version}-{port}".format(**self.info)
63
101
  self._start_free = gc.mem_free() # type: ignore
64
102
 
65
103
  if path:
@@ -69,11 +107,11 @@ class Stubber:
69
107
  path = get_root()
70
108
 
71
109
  self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/")
72
- self.log.debug(self.path)
110
+ # log.debug(self.path)
73
111
  try:
74
112
  ensure_folder(path + "/")
75
113
  except OSError:
76
- self.log.error("error creating stub folder {}".format(path))
114
+ log.error("error creating stub folder {}".format(path))
77
115
  self.problematic = [
78
116
  "upip",
79
117
  "upysh",
@@ -92,20 +130,23 @@ class Stubber:
92
130
  ]
93
131
  # there is no option to discover modules from micropython, list is read from an external file.
94
132
  self.modules = [] # type: list[str]
133
+ self._json_name = None
134
+ self._json_first = False
95
135
 
96
136
  def get_obj_attributes(self, item_instance: object):
97
137
  "extract information of the objects members and attributes"
98
138
  # name_, repr_(value), type as text, item_instance
99
139
  _result = []
100
140
  _errors = []
101
- self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
141
+ # log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
102
142
  for name in dir(item_instance):
103
- if name.startswith("_") and not name in self.modules:
143
+ if name.startswith("__") and not name in self.modules:
104
144
  continue
105
- self.log.debug("get attribute {}".format(name))
145
+ # log.debug("get attribute {}".format(name))
106
146
  try:
107
147
  val = getattr(item_instance, name)
108
148
  # name , item_repr(value) , type as text, item_instance, order
149
+ # log.debug("attribute {}:{}".format(name, val))
109
150
  try:
110
151
  type_text = repr(type(val)).split("'")[1]
111
152
  except IndexError:
@@ -138,21 +179,23 @@ class Stubber:
138
179
 
139
180
  def create_all_stubs(self):
140
181
  "Create stubs for all configured modules"
141
- self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid))
182
+ log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid))
183
+ self.report_start()
142
184
  gc.collect()
143
185
  for module_name in self.modules:
144
186
  self.create_one_stub(module_name)
145
- self.log.info("Finally done")
187
+ self.report_end()
188
+ log.info("Finally done")
146
189
 
147
190
  def create_one_stub(self, module_name: str):
148
191
  if module_name in self.problematic:
149
- self.log.warning("Skip module: {:<25} : Known problematic".format(module_name))
192
+ log.warning("Skip module: {:<25} : Known problematic".format(module_name))
150
193
  return False
151
194
  if module_name in self.excluded:
152
- self.log.warning("Skip module: {:<25} : Excluded".format(module_name))
195
+ log.warning("Skip module: {:<25} : Excluded".format(module_name))
153
196
  return False
154
197
 
155
- file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/"))
198
+ file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/"))
156
199
  gc.collect()
157
200
  result = False
158
201
  try:
@@ -167,10 +210,10 @@ class Stubber:
167
210
 
168
211
  Args:
169
212
  - module_name (str): name of the module to document. This module will be imported.
170
- - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name.
213
+ - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name.
171
214
  """
172
215
  if file_name is None:
173
- fname = module_name.replace(".", "_") + ".py"
216
+ fname = module_name.replace(".", "_") + ".pyi"
174
217
  file_name = self.path + "/" + fname
175
218
  else:
176
219
  fname = file_name.split("/")[-1]
@@ -184,10 +227,10 @@ class Stubber:
184
227
  try:
185
228
  new_module = __import__(module_name, None, None, ("*"))
186
229
  m1 = gc.mem_free() # type: ignore
187
- self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
230
+ log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
188
231
 
189
232
  except ImportError:
190
- self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
233
+ # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
191
234
  return False
192
235
 
193
236
  # Start a new file
@@ -197,21 +240,18 @@ class Stubber:
197
240
  info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}")
198
241
  s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__)
199
242
  fp.write(s)
200
- fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n")
243
+ fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n")
201
244
  self.write_object_stub(fp, new_module, module_name, "")
202
245
 
203
- self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/")))
246
+ self.report_add(module_name, file_name)
204
247
 
205
248
  if module_name not in {"os", "sys", "logging", "gc"}:
206
249
  # try to unload the module unless we use it
207
250
  try:
208
251
  del new_module
209
252
  except (OSError, KeyError): # lgtm [py/unreachable-statement]
210
- self.log.warning("could not del new_module")
211
- try:
212
- del sys.modules[module_name]
213
- except KeyError:
214
- self.log.debug("could not del sys.modules[{}]".format(module_name))
253
+ log.warning("could not del new_module")
254
+ # do not try to delete from sys.modules - most times it does not work anyway
215
255
  gc.collect()
216
256
  return True
217
257
 
@@ -219,14 +259,14 @@ class Stubber:
219
259
  "Write a module/object stub to an open file. Can be called recursive."
220
260
  gc.collect()
221
261
  if object_expr in self.problematic:
222
- self.log.warning("SKIPPING problematic module:{}".format(object_expr))
262
+ log.warning("SKIPPING problematic module:{}".format(object_expr))
223
263
  return
224
264
 
225
- # self.log.debug("DUMP : {}".format(object_expr))
265
+ # # log.debug("DUMP : {}".format(object_expr))
226
266
  items, errors = self.get_obj_attributes(object_expr)
227
267
 
228
268
  if errors:
229
- self.log.error(errors)
269
+ log.error(errors)
230
270
 
231
271
  for item_name, item_repr, item_type_txt, item_instance, _ in items:
232
272
  # name_, repr_(value), type as text, item_instance, order
@@ -234,11 +274,16 @@ class Stubber:
234
274
  # do not create stubs for these primitives
235
275
  continue
236
276
  if item_name[0].isdigit():
237
- self.log.warning("NameError: invalid name {}".format(item_name))
277
+ log.warning("NameError: invalid name {}".format(item_name))
238
278
  continue
239
279
  # Class expansion only on first 3 levels (bit of a hack)
240
- if item_type_txt == "<class 'type'>" and len(indent) <= _MAX_CLASS_LEVEL * 4:
241
- self.log.debug("{0}class {1}:".format(indent, item_name))
280
+ if (
281
+ item_type_txt == "<class 'type'>"
282
+ and len(indent) <= _MAX_CLASS_LEVEL * 4
283
+ # and not obj_name.endswith(".Pin")
284
+ # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms
285
+ ):
286
+ # log.debug("{0}class {1}:".format(indent, item_name))
242
287
  superclass = ""
243
288
  is_exception = (
244
289
  item_name.endswith("Exception")
@@ -257,11 +302,11 @@ class Stubber:
257
302
  if is_exception:
258
303
  s += indent + " ...\n"
259
304
  fp.write(s)
260
- return
305
+ continue
261
306
  # write classdef
262
307
  fp.write(s)
263
308
  # first write the class literals and methods
264
- self.log.debug("# recursion over class {0}".format(item_name))
309
+ # log.debug("# recursion over class {0}".format(item_name))
265
310
  self.write_object_stub(
266
311
  fp,
267
312
  item_instance,
@@ -275,7 +320,7 @@ class Stubber:
275
320
  s += indent + " ...\n\n"
276
321
  fp.write(s)
277
322
  elif any(word in item_type_txt for word in ["method", "function", "closure"]):
278
- self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
323
+ # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
279
324
  # module Function or class method
280
325
  # will accept any number of params
281
326
  # return type Any/Incomplete
@@ -291,7 +336,7 @@ class Stubber:
291
336
  s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret)
292
337
  s += indent + " ...\n\n"
293
338
  fp.write(s)
294
- self.log.debug("\n" + s)
339
+ # log.debug("\n" + s)
295
340
  elif item_type_txt == "<class 'module'>":
296
341
  # Skip imported modules
297
342
  # fp.write("# import {}\n".format(item_name))
@@ -301,36 +346,42 @@ class Stubber:
301
346
  t = item_type_txt[8:-2]
302
347
  s = ""
303
348
 
304
- if t in ["str", "int", "float", "bool", "bytearray", "bytes"]:
349
+ if t in ("str", "int", "float", "bool", "bytearray", "bytes"):
305
350
  # known type: use actual value
306
- s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
307
- elif t in ["dict", "list", "tuple"]:
351
+ # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
352
+ s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t)
353
+ elif t in ("dict", "list", "tuple"):
308
354
  # dict, list , tuple: use empty value
309
355
  ev = {"dict": "{}", "list": "[]", "tuple": "()"}
310
- s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
356
+ # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
357
+ s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t)
311
358
  else:
312
359
  # something else
313
- if t not in ["object", "set", "frozenset"]:
314
- # Possibly default others to item_instance object ?
360
+ if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO"
315
361
  # https://docs.python.org/3/tutorial/classes.html#item_instance-objects
362
+ # use these types for the attribute
363
+ if t == "generator":
364
+ t = "Generator"
365
+ s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
366
+ else:
367
+ # Requires Python 3.6 syntax, which is OK for the stubs/pyi
316
368
  t = "Incomplete"
317
- # Requires Python 3.6 syntax, which is OK for the stubs/pyi
318
- s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
369
+ s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
319
370
  fp.write(s)
320
- self.log.debug("\n" + s)
371
+ # log.debug("\n" + s)
321
372
  else:
322
373
  # keep only the name
323
- self.log.debug("# all other, type = '{0}'".format(item_type_txt))
374
+ # log.debug("# all other, type = '{0}'".format(item_type_txt))
324
375
  fp.write("# all other, type = '{0}'\n".format(item_type_txt))
325
376
 
326
377
  fp.write(indent + item_name + " # type: Incomplete\n")
327
378
 
328
- del items
329
- del errors
330
- try:
331
- del item_name, item_repr, item_type_txt, item_instance # type: ignore
332
- except (OSError, KeyError, NameError):
333
- pass
379
+ # del items
380
+ # del errors
381
+ # try:
382
+ # del item_name, item_repr, item_type_txt, item_instance # type: ignore
383
+ # except (OSError, KeyError, NameError):
384
+ # pass
334
385
 
335
386
  @property
336
387
  def flat_fwid(self):
@@ -346,7 +397,7 @@ class Stubber:
346
397
  "Remove all files from the stub folder"
347
398
  if path is None:
348
399
  path = self.path
349
- self.log.info("Clean/remove files in folder: {}".format(path))
400
+ log.info("Clean/remove files in folder: {}".format(path))
350
401
  try:
351
402
  os.stat(path) # TEMP workaround mpremote listdir bug -
352
403
  items = os.listdir(path)
@@ -364,41 +415,53 @@ class Stubber:
364
415
  except OSError:
365
416
  pass
366
417
 
367
- def report(self, filename: str = "modules.json"):
368
- "create json with list of exported modules"
369
- self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path))
370
- f_name = "{}/{}".format(self.path, filename)
371
- self.log.info("Report file: {}".format(f_name))
418
+ def report_start(self, filename: str = "modules.json"):
419
+ """Start a report of the modules that have been stubbed
420
+ "create json with list of exported modules"""
421
+ self._json_name = "{}/{}".format(self.path, filename)
422
+ self._json_first = True
423
+ ensure_folder(self._json_name)
424
+ log.info("Report file: {}".format(self._json_name))
372
425
  gc.collect()
373
426
  try:
374
427
  # write json by node to reduce memory requirements
375
- with open(f_name, "w") as f:
376
- self.write_json_header(f)
377
- first = True
378
- for n in self._report:
379
- self.write_json_node(f, n, first)
380
- first = False
381
- self.write_json_end(f)
382
- used = self._start_free - gc.mem_free() # type: ignore
383
- self.log.info("Memory used: {0} Kb".format(used // 1024))
384
- except OSError:
385
- self.log.error("Failed to create the report.")
386
-
387
- def write_json_header(self, f):
388
- f.write("{")
389
- f.write(dumps({"firmware": self.info})[1:-1])
390
- f.write(",\n")
391
- f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
392
- f.write(",\n")
393
- f.write('"modules" :[\n')
428
+ with open(self._json_name, "w") as f:
429
+ f.write("{")
430
+ f.write(dumps({"firmware": self.info})[1:-1])
431
+ f.write(",\n")
432
+ f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
433
+ f.write(",\n")
434
+ f.write('"modules" :[\n')
435
+
436
+ except OSError as e:
437
+ log.error("Failed to create the report.")
438
+ self._json_name = None
439
+ raise e
440
+
441
+ def report_add(self, module_name: str, stub_file: str):
442
+ "Add a module to the report"
443
+ # write json by node to reduce memory requirements
444
+ if not self._json_name:
445
+ raise Exception("No report file")
446
+ try:
447
+ with open(self._json_name, "a") as f:
448
+ if not self._json_first:
449
+ f.write(",\n")
450
+ else:
451
+ self._json_first = False
452
+ line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/"))
453
+ f.write(line)
394
454
 
395
- def write_json_node(self, f, n, first):
396
- if not first:
397
- f.write(",\n")
398
- f.write(n)
455
+ except OSError:
456
+ log.error("Failed to create the report.")
399
457
 
400
- def write_json_end(self, f):
401
- f.write("\n]}")
458
+ def report_end(self):
459
+ if not self._json_name:
460
+ raise Exception("No report file")
461
+ with open(self._json_name, "a") as f:
462
+ f.write("\n]}")
463
+ # is used as sucess indicator
464
+ log.info("Path: {}".format(self.path))
402
465
 
403
466
 
404
467
  def ensure_folder(path: str):
@@ -424,23 +487,32 @@ def ensure_folder(path: str):
424
487
 
425
488
 
426
489
  def _build(s):
427
- # extract a build nr from a string
490
+ # extract build from sys.version or os.uname().version if available
491
+ # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f'
492
+ # sys.implementation.version: 'v1.13-103-gb137d064e'
428
493
  if not s:
429
494
  return ""
430
- if " on " in s:
431
- s = s.split(" on ", 1)[0]
432
- return s.split("-")[1] if "-" in s else ""
495
+ s = s.split(" on ", 1)[0] if " on " in s else s
496
+ if s.startswith("v"):
497
+ if not "-" in s:
498
+ return ""
499
+ b = s.split("-")[1]
500
+ return b
501
+ if not "-preview" in s:
502
+ return ""
503
+ b = s.split("-preview")[1].split(".")[1]
504
+ return b
433
505
 
434
506
 
435
507
  def _info(): # type:() -> dict[str, str]
436
508
  info = OrderedDict(
437
509
  {
438
- "family": sys.implementation.name,
510
+ "family": sys.implementation.name, # type: ignore
439
511
  "version": "",
440
512
  "build": "",
441
513
  "ver": "",
442
514
  "port": sys.platform, # port: esp32 / win32 / linux / stm32
443
- "board": "GENERIC",
515
+ "board": "UNKNOWN",
444
516
  "cpu": "",
445
517
  "mpy": "",
446
518
  "arch": "",
@@ -454,56 +526,44 @@ def _info(): # type:() -> dict[str, str]
454
526
  elif info["port"] == "linux":
455
527
  info["port"] = "unix"
456
528
  try:
457
- info["version"] = ".".join([str(n) for n in sys.implementation.version])
529
+ info["version"] = version_str(sys.implementation.version) # type: ignore
458
530
  except AttributeError:
459
531
  pass
460
532
  try:
461
- machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine
462
- info["board"] = machine.strip()
463
- info["cpu"] = machine.split("with")[1].strip()
533
+ _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore
534
+ # info["board"] = "with".join(_machine.split("with")[:-1]).strip()
535
+ info["board"] = _machine
536
+ info["cpu"] = _machine.split("with")[-1].strip()
464
537
  info["mpy"] = (
465
- sys.implementation._mpy
538
+ sys.implementation._mpy # type: ignore
466
539
  if "_mpy" in dir(sys.implementation)
467
- else sys.implementation.mpy
540
+ else sys.implementation.mpy # type: ignore
468
541
  if "mpy" in dir(sys.implementation)
469
542
  else ""
470
543
  )
471
544
  except (AttributeError, IndexError):
472
545
  pass
473
- gc.collect()
474
- for filename in [d + "/board_info.csv" for d in LIBS]:
475
- # print("look up the board name in the file", filename)
476
- if file_exists(filename):
477
- # print("Found board info file: {}".format(filename))
478
- b = info["board"].strip()
479
- if find_board(info, b, filename):
480
- break
481
- if "with" in b:
482
- b = b.split("with")[0].strip()
483
- if find_board(info, b, filename):
484
- break
485
- info["board"] = "GENERIC"
486
- info["board"] = info["board"].replace(" ", "_")
487
- gc.collect()
546
+ info["board"] = get_boardname()
488
547
 
489
548
  try:
490
- # extract build from uname().version if available
491
- info["build"] = _build(os.uname()[3])
492
- if not info["build"]:
493
- # extract build from uname().release if available
494
- info["build"] = _build(os.uname()[2])
495
- if not info["build"] and ";" in sys.version:
496
- # extract build from uname().release if available
497
- info["build"] = _build(sys.version.split(";")[1])
498
- except (AttributeError, IndexError):
549
+ if "uname" in dir(os): # old
550
+ # extract build from uname().version if available
551
+ info["build"] = _build(os.uname()[3]) # type: ignore
552
+ if not info["build"]:
553
+ # extract build from uname().release if available
554
+ info["build"] = _build(os.uname()[2]) # type: ignore
555
+ elif "version" in dir(sys): # new
556
+ # extract build from sys.version if available
557
+ info["build"] = _build(sys.version)
558
+ except (AttributeError, IndexError, TypeError):
499
559
  pass
500
560
  # avoid build hashes
501
- if info["build"] and len(info["build"]) > 5:
502
- info["build"] = ""
561
+ # if info["build"] and len(info["build"]) > 5:
562
+ # info["build"] = ""
503
563
 
504
564
  if info["version"] == "" and sys.platform not in ("unix", "win32"):
505
565
  try:
506
- u = os.uname()
566
+ u = os.uname() # type: ignore
507
567
  info["version"] = u.release
508
568
  except (IndexError, AttributeError, TypeError):
509
569
  pass
@@ -525,13 +585,14 @@ def _info(): # type:() -> dict[str, str]
525
585
  info["release"] = "2.0.0"
526
586
 
527
587
  if info["family"] == "micropython":
588
+ info["version"]
528
589
  if (
529
590
  info["version"]
530
591
  and info["version"].endswith(".0")
531
592
  and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0
532
593
  and info["version"] <= "1.19.9"
533
594
  ):
534
- # drop the .0 for newer releases
595
+ # versions from 1.10.0 to 1.20.0 do not have a micro .0
535
596
  info["version"] = info["version"][:-2]
536
597
 
537
598
  # spell-checker: disable
@@ -555,25 +616,31 @@ def _info(): # type:() -> dict[str, str]
555
616
  info["arch"] = arch
556
617
  # .mpy version.minor
557
618
  info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3)
619
+ if info["build"] and not info["version"].endswith("-preview"):
620
+ info["version"] = info["version"] + "-preview"
558
621
  # simple to use version[-build] string
559
- info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}"
622
+ info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}"
560
623
 
561
624
  return info
562
625
 
563
626
 
564
- def find_board(info: dict, board_descr: str, filename: str):
565
- "Find the board in the provided board_info.csv file"
566
- with open(filename, "r") as file:
567
- # ugly code to make testable in python and micropython
568
- while 1:
569
- line = file.readline()
570
- if not line:
571
- break
572
- descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip()
573
- if descr_ == board_descr:
574
- info["board"] = board_
575
- return True
576
- return False
627
+ def version_str(version: tuple): # -> str:
628
+ v_str = ".".join([str(n) for n in version[:3]])
629
+ if len(version) > 3 and version[3]:
630
+ v_str += "-" + version[3]
631
+ return v_str
632
+
633
+
634
+ def get_boardname() -> str:
635
+ "Read the board name from the boardname.py file that may have been created upfront"
636
+ try:
637
+ from boardname import BOARDNAME # type: ignore
638
+
639
+ log.info("Found BOARDNAME: {}".format(BOARDNAME))
640
+ except ImportError:
641
+ log.warning("BOARDNAME not found")
642
+ BOARDNAME = ""
643
+ return BOARDNAME
577
644
 
578
645
 
579
646
  def get_root() -> str: # sourcery skip: use-assigned-variable
@@ -626,10 +693,6 @@ def is_micropython() -> bool:
626
693
  # pylint: disable=unused-variable,eval-used
627
694
  try:
628
695
  # either test should fail on micropython
629
- # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces
630
- # Micropython : SyntaxError
631
- # a = eval("1and 0") # lgtm [py/unused-local-variable]
632
- # Eval blocks some minification aspects
633
696
 
634
697
  # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented
635
698
  # Micropython: NotImplementedError
@@ -651,40 +714,40 @@ def main():
651
714
  stubber.clean()
652
715
  # Read stubs from modulelist in the current folder or in /libs
653
716
  # fall back to default modules
654
- stubber.modules = [] # avoid duplicates
655
- for p in LIBS:
656
- try:
657
- _1 = gc.mem_free() # type: ignore
658
- with open(p + "/modulelist.txt") as f:
659
- print("Debug: List of modules: " + p + "/modulelist.txt")
660
- line = f.readline()
661
- while line:
662
- line = line.strip()
717
+ def get_modulelist(stubber):
718
+ # new
719
+ gc.collect()
720
+ stubber.modules = [] # avoid duplicates
721
+ for p in LIBS:
722
+ fname = p + "/modulelist.txt"
723
+ if not file_exists(fname):
724
+ continue
725
+ with open(fname) as f:
726
+ # print("DEBUG: list of modules: " + p + "/modulelist.txt")
727
+ while True:
728
+ line = f.readline().strip()
729
+ if not line:
730
+ break
663
731
  if len(line) > 0 and line[0] != "#":
664
732
  stubber.modules.append(line)
665
- line = f.readline()
666
- gc.collect() # type: ignore
667
- print("Debug: Used memory to load modulelist.txt: " + str(_1 - gc.mem_free()) + " bytes") # type: ignore
733
+ gc.collect()
734
+ print("BREAK")
668
735
  break
669
- except Exception:
670
- pass
671
- if not stubber.modules:
672
- stubber.modules = ["micropython"]
673
- print("Could not find modulelist.txt, using default modules")
736
+
737
+ if not stubber.modules:
738
+ stubber.modules = ["micropython"]
739
+ # _log.warn("Could not find modulelist.txt, using default modules")
740
+ gc.collect()
741
+
742
+ stubber.modules = [] # avoid duplicates
743
+ get_modulelist(stubber)
674
744
 
675
745
  gc.collect()
676
746
 
677
747
  stubber.create_all_stubs()
678
- stubber.report()
679
748
 
680
749
 
681
750
  if __name__ == "__main__" or is_micropython():
682
- try:
683
- log = logging.getLogger("stubber")
684
- logging.basicConfig(level=logging.INFO)
685
- # logging.basicConfig(level=logging.DEBUG)
686
- except NameError:
687
- pass
688
751
  if not file_exists("no_auto_stubber.txt"):
689
752
  try:
690
753
  gc.threshold(4 * 1024) # type: ignore