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