micropython-stubber 1.16.3__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 (49) hide show
  1. {micropython_stubber-1.16.3.dist-info → micropython_stubber-1.17.0.dist-info}/METADATA +1 -1
  2. {micropython_stubber-1.16.3.dist-info → micropython_stubber-1.17.0.dist-info}/RECORD +48 -49
  3. stubber/__init__.py +1 -1
  4. stubber/basicgit.py +11 -13
  5. stubber/board/createstubs.py +138 -97
  6. stubber/board/createstubs_db.py +211 -239
  7. stubber/board/createstubs_db_min.py +322 -844
  8. stubber/board/createstubs_db_mpy.mpy +0 -0
  9. stubber/board/createstubs_lvgl.py +91 -137
  10. stubber/board/createstubs_lvgl_min.py +87 -129
  11. stubber/board/createstubs_lvgl_mpy.mpy +0 -0
  12. stubber/board/createstubs_mem.py +164 -199
  13. stubber/board/createstubs_mem_min.py +297 -791
  14. stubber/board/createstubs_mem_mpy.mpy +0 -0
  15. stubber/board/createstubs_min.py +286 -1009
  16. stubber/board/createstubs_mpy.mpy +0 -0
  17. stubber/board/modulelist.txt +1 -2
  18. stubber/codemod/_partials/__init__.py +1 -1
  19. stubber/codemod/_partials/db_main.py +90 -72
  20. stubber/codemod/_partials/modules_reader.py +29 -17
  21. stubber/codemod/board.py +2 -4
  22. stubber/codemod/enrich.py +1 -1
  23. stubber/commands/build_cmd.py +6 -4
  24. stubber/commands/get_docstubs_cmd.py +6 -11
  25. stubber/commands/get_frozen_cmd.py +6 -11
  26. stubber/commands/switch_cmd.py +6 -4
  27. stubber/freeze/freeze_manifest_2.py +2 -1
  28. stubber/freeze/get_frozen.py +28 -13
  29. stubber/minify.py +51 -38
  30. stubber/publish/candidates.py +15 -23
  31. stubber/publish/defaults.py +2 -2
  32. stubber/publish/merge_docstubs.py +5 -7
  33. stubber/publish/missing_class_methods.py +2 -2
  34. stubber/publish/pathnames.py +2 -2
  35. stubber/publish/publish.py +2 -1
  36. stubber/publish/stubpackage.py +20 -41
  37. stubber/rst/lookup.py +9 -7
  38. stubber/rst/reader.py +2 -1
  39. stubber/stubber.py +5 -6
  40. stubber/update_fallback.py +3 -1
  41. stubber/utils/__init__.py +1 -1
  42. stubber/utils/config.py +7 -9
  43. stubber/utils/repos.py +6 -5
  44. stubber/utils/versions.py +48 -7
  45. stubber/variants.py +3 -3
  46. stubber/board/logging.py +0 -99
  47. {micropython_stubber-1.16.3.dist-info → micropython_stubber-1.17.0.dist-info}/LICENSE +0 -0
  48. {micropython_stubber-1.16.3.dist-info → micropython_stubber-1.17.0.dist-info}/WHEEL +0 -0
  49. {micropython_stubber-1.16.3.dist-info → micropython_stubber-1.17.0.dist-info}/entry_points.txt +0 -0
@@ -1,1011 +1,288 @@
1
- """
2
- Create stubs for (all) modules on a MicroPython board
3
- """
4
- # Copyright (c) 2019-2023 Jos Verlinde
5
-
6
- import gc
7
- # import logging
8
- import os
9
- import sys
1
+ z='No report file'
2
+ y='Failed to create the report.'
3
+ x='{}/{}'
4
+ w='logging'
5
+ v='sys'
6
+ u='method'
7
+ t='function'
8
+ s='bool'
9
+ r='str'
10
+ q='float'
11
+ p='int'
12
+ o='stubber'
13
+ n=TypeError
14
+ m=Exception
15
+ l=KeyError
16
+ k=sorted
17
+ j=NotImplementedError
18
+ f='pycom'
19
+ e=',\n'
20
+ d='dict'
21
+ c='list'
22
+ b='tuple'
23
+ a='micropython'
24
+ Z=repr
25
+ Y=print
26
+ V='-preview'
27
+ U=True
28
+ T='-'
29
+ S='board'
30
+ R=len
31
+ Q=open
32
+ P=IndexError
33
+ O='family'
34
+ N=ImportError
35
+ M=dir
36
+ K='port'
37
+ J='.'
38
+ I=AttributeError
39
+ H=False
40
+ G='/'
41
+ E=OSError
42
+ D=None
43
+ C='version'
44
+ B=''
45
+ import gc as F,os,sys
10
46
  from time import sleep
11
-
12
- try:
13
- from ujson import dumps
14
- except:
15
- from json import dumps
16
-
17
- try:
18
- from machine import reset # type: ignore
19
- except ImportError:
20
- pass
21
-
22
- try:
23
- from collections import OrderedDict
24
- except ImportError:
25
- from ucollections import OrderedDict # type: ignore
26
-
27
- try:
28
- from nope_machine import WDT
29
-
30
- wdt = WDT()
31
-
32
- except ImportError:
33
-
34
- class _WDT:
35
- def feed(self):
36
- pass
37
-
38
- wdt = _WDT()
39
-
40
-
41
- wdt.feed()
42
-
43
- __version__ = "v1.16.3"
44
- ENOENT = 2
45
- _MAX_CLASS_LEVEL = 2 # Max class nesting
46
- LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"]
47
-
48
-
47
+ try:from ujson import dumps
48
+ except:from json import dumps
49
+ try:from machine import reset
50
+ except N:pass
51
+ try:from collections import OrderedDict as g
52
+ except N:from ucollections import OrderedDict as g
53
+ __version__='v1.17.0'
54
+ A0=2
55
+ A1=2
56
+ A5=['lib','/lib','/sd/lib','/flash/lib',J]
57
+ class L:
58
+ INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=Y
59
+ @staticmethod
60
+ def getLogger(name):return L()
61
+ @classmethod
62
+ def basicConfig(A,level):A.level=level
63
+ def info(A,msg):
64
+ if A.level<=L.INFO:A.prnt('INFO :',msg)
65
+ def warning(A,msg):
66
+ if A.level<=L.WARNING:A.prnt('WARN :',msg)
67
+ def error(A,msg):
68
+ if A.level<=L.ERROR:A.prnt('ERROR :',msg)
69
+ A=L.getLogger(o)
70
+ L.basicConfig(level=L.INFO)
49
71
  class Stubber:
50
- "Generate stubs for modules in firmware"
51
-
52
- def __init__(self, path: str = None, firmware_id: str = None): # type: ignore
53
- try:
54
- if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore
55
- raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed")
56
- except AttributeError:
57
- pass
58
- # self.log = logging.getLogger("stubber")
59
- self._report = [] # type: list[str]
60
- self.info = _info()
61
- # self.log.info("Port: {}".format(self.info["port"]))
62
- # self.log.info("Board: {}".format(self.info["board"]))
63
- gc.collect()
64
- wdt.feed()
65
- if firmware_id:
66
- self._fwid = firmware_id.lower()
67
- else:
68
- if self.info["family"] == "micropython":
69
- self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-")
70
- else:
71
- self._fwid = "{family}-v{version}-{port}".format(**self.info)
72
- self._start_free = gc.mem_free() # type: ignore
73
-
74
- if path:
75
- if path.endswith("/"):
76
- path = path[:-1]
77
- else:
78
- path = get_root()
79
-
80
- self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/")
81
- # self.log.debug(self.path)
82
- try:
83
- ensure_folder(path + "/")
84
- except OSError:
85
- print("error creating stub folder {}".format(path))
86
- self.problematic = [
87
- "upip",
88
- "upysh",
89
- "webrepl_setup",
90
- "http_client",
91
- "http_client_ssl",
92
- "http_server",
93
- "http_server_ssl",
94
- ]
95
- self.excluded = [
96
- "webrepl",
97
- "_webrepl",
98
- "port_diag",
99
- "example_sub_led.py",
100
- "example_pub_button.py",
101
- ]
102
- # there is no option to discover modules from micropython, list is read from an external file.
103
- self.modules = [] # type: list[str]
104
-
105
- def get_obj_attributes(self, item_instance: object):
106
- "extract information of the objects members and attributes"
107
- # name_, repr_(value), type as text, item_instance
108
- _result = []
109
- _errors = []
110
- # self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
111
- for name in dir(item_instance):
112
- if name.startswith("_") and not name in self.modules:
113
- continue
114
- # self.log.debug("get attribute {}".format(name))
115
- try:
116
- val = getattr(item_instance, name)
117
- # name , item_repr(value) , type as text, item_instance, order
118
- # self.log.debug("attribute {}:{}".format(name, val))
119
- try:
120
- type_text = repr(type(val)).split("'")[1]
121
- except IndexError:
122
- type_text = ""
123
- if type_text in {"int", "float", "str", "bool", "tuple", "list", "dict"}:
124
- order = 1
125
- elif type_text in {"function", "method"}:
126
- order = 2
127
- elif type_text in ("class"):
128
- order = 3
129
- else:
130
- order = 4
131
- _result.append((name, repr(val), repr(type(val)), val, order))
132
- except AttributeError as e:
133
- _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e))
134
- except MemoryError as e:
135
- # print("MemoryError: {}".format(e))
136
- sleep(1)
137
- reset()
138
-
139
- # remove internal __
140
- # _result = sorted([i for i in _result if not (i[0].startswith("_"))], key=lambda x: x[4])
141
- _result = sorted([i for i in _result if not (i[0].startswith("__"))], key=lambda x: x[4])
142
- gc.collect()
143
- return _result, _errors
144
-
145
- def add_modules(self, modules):
146
- "Add additional modules to be exported"
147
- self.modules = sorted(set(self.modules) | set(modules))
148
-
149
- def create_all_stubs(self):
150
- "Create stubs for all configured modules"
151
- # self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid))
152
- gc.collect()
153
- for module_name in self.modules:
154
- self.create_one_stub(module_name)
155
- # self.log.info("Finally done")
156
-
157
- def create_one_stub(self, module_name: str):
158
- wdt.feed()
159
- if module_name in self.problematic:
160
- # self.log.warning("Skip module: {:<25} : Known problematic".format(module_name))
161
- return False
162
- if module_name in self.excluded:
163
- # self.log.warning("Skip module: {:<25} : Excluded".format(module_name))
164
- return False
165
-
166
- file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/"))
167
- gc.collect()
168
- result = False
169
- try:
170
- result = self.create_module_stub(module_name, file_name)
171
- except OSError:
172
- return False
173
- gc.collect()
174
- return result
175
-
176
- def create_module_stub(self, module_name: str, file_name: str = None) -> bool: # type: ignore
177
- """Create a Stub of a single python module
178
-
179
- Args:
180
- - module_name (str): name of the module to document. This module will be imported.
181
- - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name.
182
- """
183
- if file_name is None:
184
- fname = module_name.replace(".", "_") + ".py"
185
- file_name = self.path + "/" + fname
186
- else:
187
- fname = file_name.split("/")[-1]
188
-
189
- if "/" in module_name:
190
- # for nested modules
191
- module_name = module_name.replace("/", ".")
192
-
193
- # import the module (as new_module) to examine it
194
- new_module = None
195
- try:
196
- new_module = __import__(module_name, None, None, ("*"))
197
- m1 = gc.mem_free() # type: ignore
198
- # self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
199
-
200
- except ImportError:
201
- # self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
202
- return False
203
-
204
- # Start a new file
205
- ensure_folder(file_name)
206
- with open(file_name, "w") as fp:
207
- # todo: improve header
208
- info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}")
209
- s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(
210
- module_name, self._fwid, info_, __version__
211
- )
212
- fp.write(s)
213
- fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n")
214
- self.write_object_stub(fp, new_module, module_name, "")
215
-
216
- self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/")))
217
-
218
- if module_name not in {"os", "sys", "logging", "gc"}:
219
- # try to unload the module unless we use it
220
- try:
221
- del new_module
222
- except (OSError, KeyError): # lgtm [py/unreachable-statement]
223
- pass
224
- # lets not try - most times it does not work anyway
225
- # try:
226
- # del sys.modules[module_name]
227
- # except KeyError:
228
- pass
229
- gc.collect()
230
- return True
231
-
232
- def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0):
233
- "Write a module/object stub to an open file. Can be called recursive."
234
- gc.collect()
235
- if object_expr in self.problematic:
236
- # self.log.warning("SKIPPING problematic module:{}".format(object_expr))
237
- return
238
-
239
- # # self.log.debug("DUMP : {}".format(object_expr))
240
- items, errors = self.get_obj_attributes(object_expr)
241
-
242
- if errors:
243
- print(errors)
244
-
245
- for item_name, item_repr, item_type_txt, item_instance, _ in items:
246
- # name_, repr_(value), type as text, item_instance, order
247
- if item_name in ["classmethod", "staticmethod", "BaseException", "Exception"]:
248
- # do not create stubs for these primitives
249
- continue
250
- if item_name[0].isdigit():
251
- # self.log.warning("NameError: invalid name {}".format(item_name))
252
- continue
253
- # Class expansion only on first 3 levels (bit of a hack)
254
- if (
255
- item_type_txt == "<class 'type'>"
256
- and len(indent) <= _MAX_CLASS_LEVEL * 4
257
- # and not obj_name.endswith(".Pin")
258
- # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms
259
- ):
260
- # self.log.info("{0}class {1}:".format(indent, item_name))
261
- superclass = ""
262
- is_exception = (
263
- item_name.endswith("Exception")
264
- or item_name.endswith("Error")
265
- or item_name
266
- in [
267
- "KeyboardInterrupt",
268
- "StopIteration",
269
- "SystemExit",
270
- ]
271
- )
272
- if is_exception:
273
- superclass = "Exception"
274
- s = "\n{}class {}({}):\n".format(indent, item_name, superclass)
275
- # s += indent + " ''\n"
276
- if is_exception:
277
- s += indent + " ...\n"
278
- fp.write(s)
279
- continue
280
- # write classdef
281
- fp.write(s)
282
- # first write the class literals and methods
283
- # self.log.debug("# recursion over class {0}".format(item_name))
284
- self.write_object_stub(
285
- fp,
286
- item_instance,
287
- "{0}.{1}".format(obj_name, item_name),
288
- indent + " ",
289
- in_class + 1,
290
- )
291
- # end with the __init__ method to make sure that the literals are defined
292
- # Add __init__
293
- s = indent + " def __init__(self, *argv, **kwargs) -> None:\n"
294
- s += indent + " ...\n\n"
295
- fp.write(s)
296
- elif any(word in item_type_txt for word in ["method", "function", "closure"]):
297
- # self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
298
- # module Function or class method
299
- # will accept any number of params
300
- # return type Any/Incomplete
301
- ret = "Incomplete"
302
- first = ""
303
- # Self parameter only on class methods/functions
304
- if in_class > 0:
305
- first = "self, "
306
- # class method - add function decoration
307
- if "bound_method" in item_type_txt or "bound_method" in item_repr:
308
- s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(
309
- indent, item_name, ret
310
- )
311
- else:
312
- s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret)
313
- s += indent + " ...\n\n"
314
- fp.write(s)
315
- # self.log.debug("\n" + s)
316
- elif item_type_txt == "<class 'module'>":
317
- # Skip imported modules
318
- # fp.write("# import {}\n".format(item_name))
319
- pass
320
-
321
- elif item_type_txt.startswith("<class '"):
322
- t = item_type_txt[8:-2]
323
- s = ""
324
-
325
- if t in ["str", "int", "float", "bool", "bytearray", "bytes"]:
326
- # known type: use actual value
327
- s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
328
- elif t in ["dict", "list", "tuple"]:
329
- # dict, list , tuple: use empty value
330
- ev = {"dict": "{}", "list": "[]", "tuple": "()"}
331
- s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
332
- else:
333
- # something else
334
- if t in ["object", "set", "frozenset", "Pin", "FileIO"]:
335
- # https://docs.python.org/3/tutorial/classes.html#item_instance-objects
336
- # use these types for the attribute
337
- s = "{0}{1} : {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
338
- else:
339
- # Requires Python 3.6 syntax, which is OK for the stubs/pyi
340
- t = "Incomplete"
341
- s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
342
- fp.write(s)
343
- # self.log.debug("\n" + s)
344
- else:
345
- # keep only the name
346
- # self.log.debug("# all other, type = '{0}'".format(item_type_txt))
347
- fp.write("# all other, type = '{0}'\n".format(item_type_txt))
348
-
349
- fp.write(indent + item_name + " # type: Incomplete\n")
350
-
351
- # del items
352
- # del errors
353
- # try:
354
- # del item_name, item_repr, item_type_txt, item_instance # type: ignore
355
- # except (OSError, KeyError, NameError):
356
- # pass
357
-
358
- @property
359
- def flat_fwid(self):
360
- "Turn _fwid from 'v1.2.3' into '1_2_3' to be used in filename"
361
- s = self._fwid
362
- # path name restrictions
363
- chars = " .()/\\:$"
364
- for c in chars:
365
- s = s.replace(c, "_")
366
- return s
367
-
368
- def clean(self, path: str = None): # type: ignore
369
- "Remove all files from the stub folder"
370
- wdt.feed()
371
- if path is None:
372
- path = self.path
373
- # self.log.info("Clean/remove files in folder: {}".format(path))
374
- try:
375
- os.stat(path) # TEMP workaround mpremote listdir bug -
376
- items = os.listdir(path)
377
- except (OSError, AttributeError):
378
- # os.listdir fails on unix
379
- return
380
- for fn in items:
381
- item = "{}/{}".format(path, fn)
382
- try:
383
- os.remove(item)
384
- except OSError:
385
- try: # folder
386
- self.clean(item)
387
- os.rmdir(item)
388
- except OSError:
389
- pass
390
-
391
- def report(self, filename: str = "modules.json"):
392
- "create json with list of exported modules"
393
- wdt.feed()
394
- # self.log.info(
395
- # "Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)
396
- # )
397
- f_name = "{}/{}".format(self.path, filename)
398
- # self.log.info("Report file: {}".format(f_name))
399
- gc.collect()
400
- try:
401
- # write json by node to reduce memory requirements
402
- with open(f_name, "w") as f:
403
- self.write_json_header(f)
404
- first = True
405
- for n in self._report:
406
- self.write_json_node(f, n, first)
407
- first = False
408
- self.write_json_end(f)
409
- used = self._start_free - gc.mem_free() # type: ignore
410
- # self.log.info("Memory used: {0} Kb".format(used // 1024))
411
- except OSError:
412
- print("Failed to create the report.")
413
-
414
- def write_json_header(self, f):
415
- f.write("{")
416
- f.write(dumps({"firmware": self.info})[1:-1])
417
- f.write(",\n")
418
- f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
419
- f.write(",\n")
420
- f.write('"modules" :[\n')
421
-
422
- def write_json_node(self, f, n, first):
423
- if not first:
424
- f.write(",\n")
425
- f.write(n)
426
-
427
- def write_json_end(self, f):
428
- f.write("\n]}")
429
-
430
-
431
- def ensure_folder(path: str):
432
- "Create nested folders if needed"
433
- i = start = 0
434
- while i != -1:
435
- i = path.find("/", start)
436
- if i != -1:
437
- p = path[0] if i == 0 else path[:i]
438
- # p = partial folder
439
- try:
440
- _ = os.stat(p)
441
- except OSError as e:
442
- # folder does not exist
443
- if e.args[0] == ENOENT:
444
- try:
445
- os.mkdir(p)
446
- except OSError as e2:
447
- print("failed to create folder {}".format(p))
448
- raise e2
449
- # next level deep
450
- start = i + 1
451
-
452
-
453
- def _build(s):
454
- # extract build from sys.version or os.uname().version if available
455
- # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f'
456
- # sys.implementation.version: 'v1.13-103-gb137d064e'
457
- if not s:
458
- return ""
459
- s = s.split(" on ", 1)[0] if " on " in s else s
460
- if s.startswith("v"):
461
- if not "-" in s:
462
- return ""
463
- b = s.split("-")[1]
464
- return b
465
- if not "-preview" in s:
466
- return ""
467
- b = s.split("-preview")[1].split(".")[1]
468
- return b
469
-
470
-
471
- def _info(): # type:() -> dict[str, str]
472
- info = OrderedDict(
473
- {
474
- "family": sys.implementation.name,
475
- "version": "",
476
- "build": "",
477
- "ver": "",
478
- "port": sys.platform, # port: esp32 / win32 / linux / stm32
479
- "board": "UNKNOWN",
480
- "cpu": "",
481
- "mpy": "",
482
- "arch": "",
483
- }
484
- )
485
- # change port names to be consistent with the repo
486
- if info["port"].startswith("pyb"):
487
- info["port"] = "stm32"
488
- elif info["port"] == "win32":
489
- info["port"] = "windows"
490
- elif info["port"] == "linux":
491
- info["port"] = "unix"
492
- try:
493
- info["version"] = version_str(sys.implementation.version) # type: ignore
494
- except AttributeError:
495
- pass
496
- try:
497
- _machine = (
498
- sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore
499
- )
500
- # info["board"] = "with".join(_machine.split("with")[:-1]).strip()
501
- info["board"] = _machine
502
- info["cpu"] = _machine.split("with")[-1].strip()
503
- info["mpy"] = (
504
- sys.implementation._mpy
505
- if "_mpy" in dir(sys.implementation)
506
- else sys.implementation.mpy
507
- if "mpy" in dir(sys.implementation)
508
- else ""
509
- )
510
- except (AttributeError, IndexError):
511
- pass
512
- gc.collect()
513
- read_boardname(info)
514
- gc.collect()
515
-
516
- try:
517
- if "uname" in dir(os): # old
518
- # extract build from uname().version if available
519
- info["build"] = _build(os.uname()[3]) # type: ignore
520
- if not info["build"]:
521
- # extract build from uname().release if available
522
- info["build"] = _build(os.uname()[2]) # type: ignore
523
- elif "version" in dir(sys): # new
524
- # extract build from sys.version if available
525
- info["build"] = _build(sys.version)
526
- except (AttributeError, IndexError, TypeError):
527
- pass
528
- # avoid build hashes
529
- # if info["build"] and len(info["build"]) > 5:
530
- # info["build"] = ""
531
-
532
- if info["version"] == "" and sys.platform not in ("unix", "win32"):
533
- try:
534
- u = os.uname() # type: ignore
535
- info["version"] = u.release
536
- except (IndexError, AttributeError, TypeError):
537
- pass
538
- # detect families
539
- for fam_name, mod_name, mod_thing in [
540
- ("pycopy", "pycopy", "const"),
541
- ("pycom", "pycom", "FAT"),
542
- ("ev3-pybricks", "pybricks.hubs", "EV3Brick"),
543
- ]:
544
- try:
545
- _t = __import__(mod_name, None, None, (mod_thing))
546
- info["family"] = fam_name
547
- del _t
548
- break
549
- except (ImportError, KeyError):
550
- pass
551
-
552
- if info["family"] == "ev3-pybricks":
553
- info["release"] = "2.0.0"
554
-
555
- if info["family"] == "micropython":
556
- info["version"]
557
- if (
558
- info["version"]
559
- and info["version"].endswith(".0")
560
- and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0
561
- and info["version"] <= "1.19.9"
562
- ):
563
- # versions from 1.10.0 to 1.20.0 do not have a micro .0
564
- info["version"] = info["version"][:-2]
565
-
566
- # spell-checker: disable
567
- if "mpy" in info and info["mpy"]: # mpy on some v1.11+ builds
568
- sys_mpy = int(info["mpy"])
569
- # .mpy architecture
570
- arch = [
571
- None,
572
- "x86",
573
- "x64",
574
- "armv6",
575
- "armv6m",
576
- "armv7m",
577
- "armv7em",
578
- "armv7emsp",
579
- "armv7emdp",
580
- "xtensa",
581
- "xtensawin",
582
- ][sys_mpy >> 10]
583
- if arch:
584
- info["arch"] = arch
585
- # .mpy version.minor
586
- info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3)
587
- if info["build"] and not info["version"].endswith("-preview"):
588
- info["version"] = info["version"] + "-preview"
589
- # simple to use version[-build] string
590
- info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}"
591
-
592
- return info
593
-
594
-
595
- def version_str(version: tuple): # -> str:
596
- v_str = ".".join([str(n) for n in version[:3]])
597
- if len(version) > 3 and version[3]:
598
- v_str += "-" + version[3]
599
- return v_str
600
-
601
-
602
- def read_boardname(info, desc: str = ""):
603
- info["board"] = info["board"].replace(" ", "_")
604
- found = False
605
- for filename in [d + "/board_name.txt" for d in LIBS]:
606
- wdt.feed()
607
- # # print("look up the board name in the file", filename)
608
- if file_exists(filename):
609
- with open(filename, "r") as file:
610
- data = file.read()
611
- if data:
612
- info["board"] = data.strip()
613
- found = True
614
- break
615
- if not found:
616
- # print("Board not found, guessing board name")
617
- descr = ""
618
- # descr = desc or info["board"].strip()
619
- # if "with " + info["cpu"].upper() in descr:
620
- # # remove the with cpu part
621
- # descr = descr.split("with " + info["cpu"].upper())[0].strip()
622
- info["board"] = descr
623
-
624
-
625
- # def read_boardname(info, desc: str = ""):
626
- # wdt.feed()
627
- # # # print("look up the board name in the file", filename)
628
- # if file_exists(filename):
629
- # descr = desc or info["board"].strip()
630
- # pos = descr.rfind(" with")
631
- # if pos != -1:
632
- # short_descr = descr[:pos].strip()
633
- # else:
634
- # short_descr = ""
635
- # # print("searching info file: {} for: '{}' or '{}'".format(filename, descr, short_descr))
636
- # if find_board(info, descr, filename, short_descr):
637
- # found = True
638
- # break
639
- # if not found:
640
- # # print("Board not found, guessing board name")
641
- # descr = desc or info["board"].strip()
642
- # if "with " + info["cpu"].upper() in descr:
643
- # # remove the with cpu part
644
- # descr = descr.split("with " + info["cpu"].upper())[0].strip()
645
- # info["board"] = descr
646
- # info["board"] = info["board"].replace(" ", "_")
647
- # gc.collect()
648
-
649
-
650
- # def find_board(info: dict, descr: str, filename: str, short_descr: str):
651
- # "Find the board in the provided board_info.csv file"
652
- # short_hit = ""
653
- # with open(filename, "r") as file:
654
- # # ugly code to make testable in python and micropython
655
- # # TODO: This is VERY slow on micropython whith MPREMOTE mount on esp32 (2-3 minutes to read file)
656
- # while 1:
657
- # line = file.readline()
658
- # if not line:
659
- # break
660
- # descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip()
661
- # if descr_ == descr:
662
- # info["board"] = board_
663
- # return True
664
- # elif short_descr and descr_ == short_descr:
665
- # if "with" in short_descr:
666
- # # Good enough - no need to trawl the entire file
667
- # info["board"] = board_
668
- # return True
669
- # # good enough if not found in the rest of the file (but slow)
670
- # short_hit = board_
671
- # if short_hit:
672
- # info["board"] = short_hit
673
- # return True
674
- # return False
675
-
676
-
677
- def get_root() -> str: # sourcery skip: use-assigned-variable
678
- "Determine the root folder of the device"
679
- try:
680
- c = os.getcwd()
681
- except (OSError, AttributeError):
682
- # unix port
683
- c = "."
684
- r = c
685
- for r in [c, "/sd", "/flash", "/", "."]:
686
- try:
687
- _ = os.stat(r)
688
- break
689
- except OSError:
690
- continue
691
- return r
692
-
693
-
694
- def file_exists(filename: str):
695
- try:
696
- if os.stat(filename)[0] >> 14:
697
- return True
698
- return False
699
- except OSError:
700
- return False
701
-
702
-
703
- def show_help():
704
- # print("-p, --path path to store the stubs in, defaults to '.'")
705
- sys.exit(1)
706
-
707
-
708
- def read_path() -> str:
709
- "get --path from cmdline. [unix/win]"
710
- path = ""
711
- if len(sys.argv) == 3:
712
- cmd = (sys.argv[1]).lower()
713
- if cmd in ("--path", "-p"):
714
- path = sys.argv[2]
715
- else:
716
- show_help()
717
- elif len(sys.argv) == 2:
718
- show_help()
719
- return path
720
-
721
-
722
- def is_micropython() -> bool:
723
- "runtime test to determine full or micropython"
724
- # pylint: disable=unused-variable,eval-used
725
- try:
726
- # either test should fail on micropython
727
- # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces
728
- # Micropython : SyntaxError
729
- # a = eval("1and 0") # lgtm [py/unused-local-variable]
730
- # Eval blocks some minification aspects
731
-
732
- # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented
733
- # Micropython: NotImplementedError
734
- b = bytes("abc", encoding="utf8") # type: ignore # lgtm [py/unused-local-variable]
735
-
736
- # c) https://docs.micropython.org/en/latest/genrst/core_language.html#function-objects-do-not-have-the-module-attribute
737
- # Micropython: AttributeError
738
- c = is_micropython.__module__ # type: ignore # lgtm [py/unused-local-variable]
739
- return False
740
- except (NotImplementedError, AttributeError):
741
- return True
742
-
743
-
744
- def main():
745
- stubber = Stubber(path=read_path())
746
- # stubber = Stubber(path="/sd")
747
- # Option: Specify a firmware name & version
748
- # stubber = Stubber(firmware_id='HoverBot v1.2.1')
749
- stubber.clean()
750
- # there is no option to discover modules from micropython, need to hardcode
751
- # below contains combined modules from Micropython ESP8622, ESP32, Loboris, Pycom and ulab , lvgl
752
- # spell-checker: disable
753
- # modules to stub : 131
754
- stubber.modules = [
755
- "WM8960",
756
- "_OTA",
757
- "_asyncio",
758
- "_boot_fat",
759
- "_coap",
760
- "_espnow",
761
- "_flash_control_OTA",
762
- "_main_pybytes",
763
- "_mqtt",
764
- "_mqtt_core",
765
- "_msg_handl",
766
- "_onewire",
767
- "_periodical_pin",
768
- "_pybytes",
769
- "_pybytes_ca",
770
- "_pybytes_config",
771
- "_pybytes_config_reader",
772
- "_pybytes_connection",
773
- "_pybytes_constants",
774
- "_pybytes_debug",
775
- "_pybytes_library",
776
- "_pybytes_machine_learning",
777
- "_pybytes_main",
778
- "_pybytes_protocol",
779
- "_pybytes_pyconfig",
780
- "_pybytes_pymesh_config",
781
- "_rp2",
782
- "_terminal",
783
- "_thread",
784
- "_uasyncio",
785
- "_urequest",
786
- "adcfft",
787
- "aioble/__init__",
788
- "aioble/central",
789
- "aioble/client",
790
- "aioble/core",
791
- "aioble/device",
792
- "aioble/l2cap",
793
- "aioble/peripheral",
794
- "aioble/security",
795
- "aioble/server",
796
- "aioespnow",
797
- "ak8963",
798
- "apa102",
799
- "apa106",
800
- "argparse",
801
- "array",
802
- "asyncio/__init__",
803
- "asyncio/core",
804
- "asyncio/event",
805
- "asyncio/funcs",
806
- "asyncio/lock",
807
- "asyncio/stream",
808
- "binascii",
809
- "bluetooth",
810
- "breakout_as7262",
811
- "breakout_bh1745",
812
- "breakout_bme280",
813
- "breakout_bme68x",
814
- "breakout_bmp280",
815
- "breakout_dotmatrix",
816
- "breakout_encoder",
817
- "breakout_icp10125",
818
- "breakout_ioexpander",
819
- "breakout_ltr559",
820
- "breakout_matrix11x7",
821
- "breakout_mics6814",
822
- "breakout_msa301",
823
- "breakout_paa5100",
824
- "breakout_pmw3901",
825
- "breakout_potentiometer",
826
- "breakout_rgbmatrix5x5",
827
- "breakout_rtc",
828
- "breakout_scd41",
829
- "breakout_sgp30",
830
- "breakout_trackball",
831
- "breakout_vl53l5cx",
832
- "btree",
833
- "cmath",
834
- "collections",
835
- "crypto",
836
- "cryptolib",
837
- "curl",
838
- "deflate",
839
- "dht",
840
- "display",
841
- "display_driver_utils",
842
- "ds18x20",
843
- "encoder",
844
- "errno",
845
- "esp",
846
- "esp32",
847
- "espidf",
848
- "espnow",
849
- "ffi",
850
- "flashbdev",
851
- "framebuf",
852
- "freesans20",
853
- "fs_driver",
854
- "functools",
855
- "galactic",
856
- "gc",
857
- "gfx_pack",
858
- "gsm",
859
- "hashlib",
860
- "heapq",
861
- "hub75",
862
- "ili9341",
863
- "ili9XXX",
864
- "imagetools",
865
- "inisetup",
866
- "interstate75",
867
- "io",
868
- "jpegdec",
869
- "json",
870
- "lcd160cr",
871
- "lodepng",
872
- "logging",
873
- "lsm6dsox",
874
- "lv_colors",
875
- "lv_utils",
876
- "lvgl",
877
- "lwip",
878
- "machine",
879
- "math",
880
- "microWebSocket",
881
- "microWebSrv",
882
- "microWebTemplate",
883
- "micropython",
884
- "mip",
885
- "mip/__init__",
886
- "mip/__main__",
887
- "motor",
888
- "mpu6500",
889
- "mpu9250",
890
- "neopixel",
891
- "network",
892
- "ntptime",
893
- "onewire",
894
- "os",
895
- "pcf85063a",
896
- "picoexplorer",
897
- "picographics",
898
- "picokeypad",
899
- "picoscroll",
900
- "picounicorn",
901
- "picowireless",
902
- "pimoroni",
903
- "pimoroni_bus",
904
- "pimoroni_i2c",
905
- "plasma",
906
- "platform",
907
- "pyb",
908
- "pycom",
909
- "pye",
910
- "qrcode",
911
- "queue",
912
- "random",
913
- "requests",
914
- "requests/__init__",
915
- "rp2",
916
- "rtch",
917
- "samd",
918
- "select",
919
- "servo",
920
- "socket",
921
- "ssd1306",
922
- "ssh",
923
- "ssl",
924
- "stm",
925
- "struct",
926
- "sys",
927
- "termios",
928
- "time",
929
- "tpcalib",
930
- "uarray",
931
- "uasyncio/__init__",
932
- "uasyncio/core",
933
- "uasyncio/event",
934
- "uasyncio/funcs",
935
- "uasyncio/lock",
936
- "uasyncio/stream",
937
- "uasyncio/tasks",
938
- "ubinascii",
939
- "ubluetooth",
940
- "ucollections",
941
- "ucrypto",
942
- "ucryptolib",
943
- "uctypes",
944
- "uerrno",
945
- "uftpd",
946
- "uhashlib",
947
- "uheapq",
948
- "uio",
949
- "ujson",
950
- "ulab",
951
- "ulab/approx",
952
- "ulab/compare",
953
- "ulab/fft",
954
- "ulab/filter",
955
- "ulab/linalg",
956
- "ulab/numerical",
957
- "ulab/poly",
958
- "ulab/user",
959
- "ulab/vector",
960
- "umachine",
961
- "umqtt/__init__",
962
- "umqtt/robust",
963
- "umqtt/simple",
964
- "uos",
965
- "uplatform",
966
- "uqueue",
967
- "urandom",
968
- "ure",
969
- "urequests",
970
- "urllib/urequest",
971
- "uselect",
972
- "usocket",
973
- "ussl",
974
- "ustruct",
975
- "usys",
976
- "utelnetserver",
977
- "utime",
978
- "utimeq",
979
- "uwebsocket",
980
- "uzlib",
981
- "version",
982
- "websocket",
983
- "websocket_helper",
984
- "wipy",
985
- "writer",
986
- "xpt2046",
987
- "ymodem",
988
- "zephyr",
989
- "zlib",
990
- ] # spell-checker: enable
991
-
992
- gc.collect()
993
-
994
- stubber.create_all_stubs()
995
- stubber.report()
996
-
997
-
998
- if __name__ == "__main__" or is_micropython():
999
- try:
1000
- log = logging.getLogger("stubber")
1001
- logging.basicConfig(level=logging.INFO)
1002
- # logging.basicConfig(level=logging.DEBUG)
1003
- except NameError:
1004
- pass
1005
- if not file_exists("no_auto_stubber.txt"):
1006
- try:
1007
- gc.threshold(4 * 1024) # type: ignore
1008
- gc.enable()
1009
- except BaseException:
1010
- pass
1011
- main()
72
+ def __init__(B,path=D,firmware_id=D):
73
+ C=firmware_id
74
+ try:
75
+ if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed')
76
+ except I:pass
77
+ B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[S]));F.collect()
78
+ if C:B._fwid=C.lower()
79
+ elif B.info[O]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(T)
80
+ else:B._fwid='{family}-v{version}-{port}'.format(**B.info)
81
+ B._start_free=F.mem_free()
82
+ if path:
83
+ if path.endswith(G):path=path[:-1]
84
+ else:path=get_root()
85
+ B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G)
86
+ try:W(path+G)
87
+ except E:A.error('error creating stub folder {}'.format(path))
88
+ B.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];B.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];B.modules=[];B._json_name=D;B._json_first=H
89
+ def get_obj_attributes(L,item_instance):
90
+ H=item_instance;C=[];K=[]
91
+ for A in M(H):
92
+ if A.startswith('__')and not A in L.modules:continue
93
+ try:
94
+ D=getattr(H,A)
95
+ try:E=Z(type(D)).split("'")[1]
96
+ except P:E=B
97
+ if E in{p,q,r,s,b,c,d}:G=1
98
+ elif E in{t,u}:G=2
99
+ elif E in'class':G=3
100
+ else:G=4
101
+ C.append((A,Z(D),Z(type(D)),D,G))
102
+ except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J))
103
+ except MemoryError as J:Y('MemoryError: {}'.format(J));sleep(1);reset()
104
+ C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K
105
+ def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules))
106
+ def create_all_stubs(B):
107
+ A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect()
108
+ for C in B.modules:B.create_one_stub(C)
109
+ B.report_end();A.info('Finally done')
110
+ def create_one_stub(C,module_name):
111
+ B=module_name
112
+ if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H
113
+ if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H
114
+ I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();D=H
115
+ try:D=C.create_module_stub(B,I)
116
+ except E:return H
117
+ F.collect();return D
118
+ def create_module_stub(K,module_name,file_name=D):
119
+ I=file_name;C=module_name
120
+ if I is D:L=C.replace(J,'_')+'.pyi';I=K.path+G+L
121
+ else:L=I.split(G)[-1]
122
+ if G in C:C=C.replace(G,J)
123
+ M=D
124
+ try:M=__import__(C,D,D,'*');P=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,P))
125
+ except N:return H
126
+ W(I)
127
+ with Q(I,'w')as O:R=str(K.info).replace('OrderedDict(',B).replace('})','}');S='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(C,K._fwid,R,__version__);O.write(S);O.write('from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n');K.write_object_stub(O,M,C,B)
128
+ K.report_add(C,I)
129
+ if C not in{'os',v,w,'gc'}:
130
+ try:del M
131
+ except(E,l):A.warning('could not del new_module')
132
+ F.collect();return U
133
+ def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0):
134
+ X='generator';W='{0}{1}: {3} = {2}\n';V='bound_method';U='Incomplete';N=in_class;M='Exception';L=object_expr;I=fp;D=indent;F.collect()
135
+ if L in K.problematic:A.warning('SKIPPING problematic module:{}'.format(L));return
136
+ Y,O=K.get_obj_attributes(L)
137
+ if O:A.error(O)
138
+ for(E,J,H,Z,e)in Y:
139
+ if E in['classmethod','staticmethod','BaseException',M]:continue
140
+ if E[0].isdigit():A.warning('NameError: invalid name {}'.format(E));continue
141
+ if H=="<class 'type'>"and R(D)<=A1*4:
142
+ P=B;Q=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit']
143
+ if Q:P=M
144
+ C='\n{}class {}({}):\n'.format(D,E,P)
145
+ if Q:C+=D+' ...\n';I.write(C);continue
146
+ I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C)
147
+ elif any(A in H for A in[u,t,'closure']):
148
+ S=U;T=B
149
+ if N>0:T='self, '
150
+ if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S)
151
+ else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S)
152
+ C+=D+' ...\n\n';I.write(C)
153
+ elif H=="<class 'module'>":0
154
+ elif H.startswith("<class '"):
155
+ G=H[8:-2];C=B
156
+ if G in(r,p,q,s,'bytearray','bytes'):C=W.format(D,E,J,G)
157
+ elif G in(d,c,b):a={d:'{}',c:'[]',b:'()'};C=W.format(D,E,a[G],G)
158
+ elif G in('object','set','frozenset','Pin',X):
159
+ if G==X:G='Generator'
160
+ C='{0}{1}: {2} ## = {4}\n'.format(D,E,G,H,J)
161
+ else:G=U;C='{0}{1}: {2} ## {3} = {4}\n'.format(D,E,G,H,J)
162
+ I.write(C)
163
+ else:I.write("# all other, type = '{0}'\n".format(H));I.write(D+E+' # type: Incomplete\n')
164
+ @property
165
+ def flat_fwid(self):
166
+ A=self._fwid;B=' .()/\\:$'
167
+ for C in B:A=A.replace(C,'_')
168
+ return A
169
+ def clean(C,path=D):
170
+ if path is D:path=C.path
171
+ A.info('Clean/remove files in folder: {}'.format(path))
172
+ try:os.stat(path);F=os.listdir(path)
173
+ except(E,I):return
174
+ for G in F:
175
+ B=x.format(path,G)
176
+ try:os.remove(B)
177
+ except E:
178
+ try:C.clean(B);os.rmdir(B)
179
+ except E:pass
180
+ def report_start(B,filename='modules.json'):
181
+ H='firmware';B._json_name=x.format(B.path,filename);B._json_first=U;W(B._json_name);A.info('Report file: {}'.format(B._json_name));F.collect()
182
+ try:
183
+ with Q(B._json_name,'w')as G:G.write('{');G.write(dumps({H:B.info})[1:-1]);G.write(e);G.write(dumps({o:{C:__version__},'stubtype':H})[1:-1]);G.write(e);G.write('"modules" :[\n')
184
+ except E as I:A.error(y);B._json_name=D;raise I
185
+ def report_add(B,module_name,stub_file):
186
+ if not B._json_name:raise m(z)
187
+ try:
188
+ with Q(B._json_name,'a')as C:
189
+ if not B._json_first:C.write(e)
190
+ else:B._json_first=H
191
+ D='{{"module": "{}", "file": "{}"}}'.format(module_name,stub_file.replace('\\',G));C.write(D)
192
+ except E:A.error(y)
193
+ def report_end(B):
194
+ if not B._json_name:raise m(z)
195
+ with Q(B._json_name,'a')as C:C.write('\n]}')
196
+ A.info('Path: {}'.format(B.path))
197
+ def W(path):
198
+ B=D=0
199
+ while B!=-1:
200
+ B=path.find(G,D)
201
+ if B!=-1:
202
+ C=path[0]if B==0 else path[:B]
203
+ try:I=os.stat(C)
204
+ except E as F:
205
+ if F.args[0]==A0:
206
+ try:os.mkdir(C)
207
+ except E as H:A.error('failed to create folder {}'.format(C));raise H
208
+ D=B+1
209
+ def X(s):
210
+ C=' on '
211
+ if not s:return B
212
+ s=s.split(C,1)[0]if C in s else s
213
+ if s.startswith('v'):
214
+ if not T in s:return B
215
+ A=s.split(T)[1];return A
216
+ if not V in s:return B
217
+ A=s.split(V)[1].split(J)[1];return A
218
+ def _info():
219
+ Y='ev3-pybricks';W='pycopy';U='unix';T='win32';R='arch';Q='cpu';L='ver';F='mpy';E='build';A=g({O:sys.implementation.name,C:B,E:B,L:B,K:sys.platform,S:'UNKNOWN',Q:B,F:B,R:B})
220
+ if A[K].startswith('pyb'):A[K]='stm32'
221
+ elif A[K]==T:A[K]='windows'
222
+ elif A[K]=='linux':A[K]=U
223
+ try:A[C]=A2(sys.implementation.version)
224
+ except I:pass
225
+ try:H=sys.implementation._machine if'_machine'in M(sys.implementation)else os.uname().machine;A[S]=H;A[Q]=H.split('with')[-1].strip();A[F]=sys.implementation._mpy if'_mpy'in M(sys.implementation)else sys.implementation.mpy if F in M(sys.implementation)else B
226
+ except(I,P):pass
227
+ A[S]=A3()
228
+ try:
229
+ if'uname'in M(os):
230
+ A[E]=X(os.uname()[3])
231
+ if not A[E]:A[E]=X(os.uname()[2])
232
+ elif C in M(sys):A[E]=X(sys.version)
233
+ except(I,P,n):pass
234
+ if A[C]==B and sys.platform not in(U,T):
235
+ try:Z=os.uname();A[C]=Z.release
236
+ except(P,I,n):pass
237
+ for(b,c,d)in[(W,W,'const'),(f,f,'FAT'),(Y,'pybricks.hubs','EV3Brick')]:
238
+ try:e=__import__(c,D,D,d);A[O]=b;del e;break
239
+ except(N,l):pass
240
+ if A[O]==Y:A['release']='2.0.0'
241
+ if A[O]==a:
242
+ A[C]
243
+ if A[C]and A[C].endswith('.0')and A[C]>='1.10.0'and A[C]<='1.19.9':A[C]=A[C][:-2]
244
+ if F in A and A[F]:
245
+ G=int(A[F]);J=[D,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10]
246
+ if J:A[R]=J
247
+ A[F]='v{}.{}'.format(G&255,G>>8&3)
248
+ if A[E]and not A[C].endswith(V):A[C]=A[C]+V
249
+ A[L]=f"{A[C]}-{A[E]}"if A[E]else f"{A[C]}";return A
250
+ def A2(version):
251
+ A=version;B=J.join([str(A)for A in A[:3]])
252
+ if R(A)>3 and A[3]:B+=T+A[3]
253
+ return B
254
+ def A3():
255
+ try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C))
256
+ except N:A.warning('BOARDNAME not found');C=B
257
+ return C
258
+ def get_root():
259
+ try:A=os.getcwd()
260
+ except(E,I):A=J
261
+ B=A
262
+ for B in[A,'/sd','/flash',G,J]:
263
+ try:C=os.stat(B);break
264
+ except E:continue
265
+ return B
266
+ def A4(filename):
267
+ try:
268
+ if os.stat(filename)[0]>>14:return U
269
+ return H
270
+ except E:return H
271
+ def h():Y("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1)
272
+ def read_path():
273
+ path=B
274
+ if R(sys.argv)==3:
275
+ A=sys.argv[1].lower()
276
+ if A in('--path','-p'):path=sys.argv[2]
277
+ else:h()
278
+ elif R(sys.argv)==2:h()
279
+ return path
280
+ def i():
281
+ try:A=bytes('abc',encoding='utf8');B=i.__module__;return H
282
+ except(j,I):return U
283
+ def main():stubber=Stubber(path=read_path());stubber.clean();stubber.modules=['WM8960','_OTA','_asyncio','_boot_fat','_coap','_espnow','_flash_control_OTA','_main_pybytes','_mqtt','_mqtt_core','_msg_handl','_onewire','_periodical_pin','_pybytes','_pybytes_ca','_pybytes_config','_pybytes_config_reader','_pybytes_connection','_pybytes_constants','_pybytes_debug','_pybytes_library','_pybytes_machine_learning','_pybytes_main','_pybytes_protocol','_pybytes_pyconfig','_pybytes_pymesh_config','_rp2','_terminal','_thread','_uasyncio','_urequest','adcfft','aioble/__init__','aioble/central','aioble/client','aioble/core','aioble/device','aioble/l2cap','aioble/peripheral','aioble/security','aioble/server','aioespnow','ak8963','apa102','apa106','argparse','array','asyncio/__init__','asyncio/core','asyncio/event','asyncio/funcs','asyncio/lock','asyncio/stream','binascii','bluetooth','breakout_as7262','breakout_bh1745','breakout_bme280','breakout_bme68x','breakout_bmp280','breakout_dotmatrix','breakout_encoder','breakout_icp10125','breakout_ioexpander','breakout_ltr559','breakout_matrix11x7','breakout_mics6814','breakout_msa301','breakout_paa5100','breakout_pmw3901','breakout_potentiometer','breakout_rgbmatrix5x5','breakout_rtc','breakout_scd41','breakout_sgp30','breakout_trackball','breakout_vl53l5cx','btree','cmath','collections','crypto','cryptolib','curl','deflate','dht','display','display_driver_utils','ds18x20','encoder','errno','esp','esp32','espidf','espnow','ffi','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',w,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',a,'mip','mip/__init__','mip/__main__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform','pyb',f,'pye','qrcode','queue','random','requests','requests/__init__','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',v,'termios','time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',C,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];F.collect();stubber.create_all_stubs()
284
+ if __name__=='__main__'or i():
285
+ if not A4('no_auto_stubber.txt'):
286
+ try:F.threshold(4*1024);F.enable()
287
+ except BaseException:pass
288
+ main()