micropython-stubber 1.19.0__py3-none-any.whl → 1.20.1__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 (41) hide show
  1. {micropython_stubber-1.19.0.dist-info → micropython_stubber-1.20.1.dist-info}/METADATA +4 -4
  2. {micropython_stubber-1.19.0.dist-info → micropython_stubber-1.20.1.dist-info}/RECORD +37 -41
  3. mpflash/README.md +41 -33
  4. mpflash/libusb_flash.ipynb +203 -203
  5. mpflash/mpflash/ask_input.py +18 -3
  6. mpflash/mpflash/cli_download.py +16 -12
  7. mpflash/mpflash/cli_flash.py +16 -7
  8. mpflash/mpflash/cli_group.py +1 -1
  9. mpflash/mpflash/cli_list.py +2 -2
  10. mpflash/mpflash/cli_main.py +4 -3
  11. mpflash/mpflash/download.py +11 -8
  12. mpflash/mpflash/flash_uf2.py +1 -1
  13. mpflash/mpflash/list.py +29 -12
  14. mpflash/mpflash/mpboard_id/board_id.py +14 -11
  15. mpflash/mpflash/mpremoteboard/__init__.py +6 -5
  16. mpflash/mpflash/mpremoteboard/runner.py +12 -12
  17. mpflash/mpflash/worklist.py +1 -1
  18. mpflash/poetry.lock +85 -84
  19. mpflash/pyproject.toml +2 -2
  20. stubber/__init__.py +1 -1
  21. stubber/board/createstubs.py +4 -3
  22. stubber/board/createstubs_db.py +4 -4
  23. stubber/board/createstubs_db_min.py +825 -329
  24. stubber/board/createstubs_db_mpy.mpy +0 -0
  25. stubber/board/createstubs_mem.py +4 -4
  26. stubber/board/createstubs_mem_min.py +765 -304
  27. stubber/board/createstubs_mem_mpy.mpy +0 -0
  28. stubber/board/createstubs_min.py +975 -293
  29. stubber/board/createstubs_mpy.mpy +0 -0
  30. stubber/board/modulelist.txt +1 -0
  31. stubber/commands/{mcu_cmd.py → get_mcu_cmd.py} +20 -3
  32. stubber/stubber.py +1 -9
  33. stubber/update_fallback.py +104 -104
  34. stubber/utils/config.py +6 -0
  35. stubber/commands/get_lobo_cmd.py +0 -58
  36. stubber/commands/minify_cmd.py +0 -60
  37. stubber/commands/upd_fallback_cmd.py +0 -36
  38. stubber/commands/upd_module_list_cmd.py +0 -18
  39. {micropython_stubber-1.19.0.dist-info → micropython_stubber-1.20.1.dist-info}/LICENSE +0 -0
  40. {micropython_stubber-1.19.0.dist-info → micropython_stubber-1.20.1.dist-info}/WHEEL +0 -0
  41. {micropython_stubber-1.19.0.dist-info → micropython_stubber-1.20.1.dist-info}/entry_points.txt +0 -0
@@ -1,332 +1,828 @@
1
- A2='No report file'
2
- A1='Failed to create the report.'
3
- A0='method'
4
- z='function'
5
- y='bool'
6
- x='str'
7
- w='float'
8
- v='int'
9
- u='micropython'
10
- t='stubber'
11
- s=Exception
12
- r=KeyError
13
- q=sorted
14
- p=MemoryError
15
- o=NotImplementedError
16
- k=',\n'
17
- j='modules.json'
18
- i='{}/{}'
19
- h='w'
20
- g='dict'
21
- f='list'
22
- e='tuple'
23
- d=TypeError
24
- c=str
25
- b=repr
26
- W='-preview'
27
- V='-'
28
- U='board'
29
- T=IndexError
30
- S=print
31
- R=True
32
- Q='family'
33
- P=len
34
- O=ImportError
35
- N=dir
36
- M=open
37
- K='port'
38
- J='.'
39
- I=AttributeError
40
- H=False
41
- G='/'
42
- E=None
43
- D=OSError
44
- C='version'
45
- B=''
46
- import gc as F,os,sys
1
+ """
2
+ Create stubs for (all) modules on a MicroPython board.
3
+
4
+ This variant of the createstubs.py script is optimized for use on very-low-memory devices.
5
+ Note: this version has undergone limited testing.
6
+
7
+ 1) reads the list of modules from a text file `modulelist.txt` that should be uploaded to the device.
8
+ 2) stored the already processed modules in a text file `modulelist.done`
9
+ 3) process the modules in the database:
10
+ - stub the module
11
+ - update the modulelist.done file
12
+ - reboots the device if it runs out of memory
13
+ 4) creates the modules.json
14
+
15
+ If that cannot be found then only a single module (micropython) is stubbed.
16
+ In order to run this on low-memory devices two additional steps are recommended:
17
+ - minification, using python-minifierto reduce overall size, and remove logging overhead.
18
+ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device
19
+
20
+
21
+ This variant was generated from createstubs.py by micropython-stubber v1.20.1
22
+ """
23
+
24
+ # Copyright (c) 2019-2024 Jos Verlinde
25
+
26
+ import gc
27
+ import os
28
+ import sys
47
29
  from time import sleep
48
- try:from ujson import dumps
49
- except:from json import dumps
50
- try:from machine import reset
51
- except O:pass
52
- try:from collections import OrderedDict as l
53
- except O:from ucollections import OrderedDict as l
54
- __version__='v1.19.0'
55
- A3=2
56
- A4=2
57
- A5=['lib','/lib','/sd/lib','/flash/lib',J]
58
- class L:
59
- INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S
60
- @staticmethod
61
- def getLogger(name):return L()
62
- @classmethod
63
- def basicConfig(A,level):A.level=level
64
- def info(A,msg):
65
- if A.level<=L.INFO:A.prnt('INFO :',msg)
66
- def warning(A,msg):
67
- if A.level<=L.WARNING:A.prnt('WARN :',msg)
68
- def error(A,msg):
69
- if A.level<=L.ERROR:A.prnt('ERROR :',msg)
70
- A=L.getLogger(t)
71
- L.basicConfig(level=L.INFO)
30
+
31
+ try:
32
+ from ujson import dumps
33
+ except:
34
+ from json import dumps
35
+
36
+ try:
37
+ from machine import reset # type: ignore
38
+ except ImportError:
39
+ pass
40
+
41
+ try:
42
+ from collections import OrderedDict
43
+ except ImportError:
44
+ from ucollections import OrderedDict # type: ignore
45
+
46
+ __version__ = "v1.20.1"
47
+ ENOENT = 2
48
+ _MAX_CLASS_LEVEL = 2 # Max class nesting
49
+ LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."]
50
+
51
+
52
+ # our own logging module to avoid dependency on and interfering with logging module
53
+ class logging:
54
+ # DEBUG = 10
55
+ INFO = 20
56
+ WARNING = 30
57
+ ERROR = 40
58
+ level = INFO
59
+ prnt = print
60
+
61
+ @staticmethod
62
+ def getLogger(name):
63
+ return logging()
64
+
65
+ @classmethod
66
+ def basicConfig(cls, level):
67
+ cls.level = level
68
+
69
+ # def debug(self, msg):
70
+ # if self.level <= logging.DEBUG:
71
+ # self.prnt("DEBUG :", msg)
72
+
73
+ def info(self, msg):
74
+ if self.level <= logging.INFO:
75
+ self.prnt("INFO :", msg)
76
+
77
+ def warning(self, msg):
78
+ if self.level <= logging.WARNING:
79
+ self.prnt("WARN :", msg)
80
+
81
+ def error(self, msg):
82
+ if self.level <= logging.ERROR:
83
+ self.prnt("ERROR :", msg)
84
+
85
+
86
+ log = logging.getLogger("stubber")
87
+ logging.basicConfig(level=logging.INFO)
88
+ # logging.basicConfig(level=logging.DEBUG)
89
+
90
+
72
91
  class Stubber:
73
- def __init__(B,path=E,firmware_id=E):
74
- C=firmware_id
75
- try:
76
- if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise o('MicroPython 1.13.0 cannot be stubbed')
77
- except I:pass
78
- B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));F.collect()
79
- if C:B._fwid=C.lower()
80
- elif B.info[Q]==u:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V)
81
- else:B._fwid='{family}-v{version}-{port}'.format(**B.info)
82
- B._start_free=F.mem_free()
83
- if path:
84
- if path.endswith(G):path=path[:-1]
85
- else:path=get_root()
86
- B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G)
87
- try:X(path+G)
88
- except D:A.error('error creating stub folder {}'.format(path))
89
- 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=E;B._json_first=H
90
- def get_obj_attributes(L,item_instance):
91
- H=item_instance;C=[];K=[]
92
- for A in N(H):
93
- if A.startswith('__')and not A in L.modules:continue
94
- try:
95
- D=getattr(H,A)
96
- try:E=b(type(D)).split("'")[1]
97
- except T:E=B
98
- if E in{v,w,x,y,e,f,g}:G=1
99
- elif E in{z,A0}:G=2
100
- elif E in'class':G=3
101
- else:G=4
102
- C.append((A,b(D),b(type(D)),D,G))
103
- except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J))
104
- except p as J:S('MemoryError: {}'.format(J));sleep(1);reset()
105
- C=q([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K
106
- def add_modules(A,modules):A.modules=q(set(A.modules)|set(modules))
107
- def create_all_stubs(B):
108
- A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect()
109
- for C in B.modules:B.create_one_stub(C)
110
- B.report_end();A.info('Finally done')
111
- def create_one_stub(C,module_name):
112
- B=module_name
113
- if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H
114
- if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H
115
- I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();E=H
116
- try:E=C.create_module_stub(B,I)
117
- except D:return H
118
- F.collect();return E
119
- def create_module_stub(K,module_name,file_name=E):
120
- I=file_name;C=module_name
121
- if I is E:L=C.replace(J,'_')+'.pyi';I=K.path+G+L
122
- else:L=I.split(G)[-1]
123
- if G in C:C=C.replace(G,J)
124
- N=E
125
- try:N=__import__(C,E,E,'*');Q=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,Q))
126
- except O:return H
127
- X(I)
128
- with M(I,h)as P:S=c(K.info).replace('OrderedDict(',B).replace('})','}');T='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(C,K._fwid,S,__version__);P.write(T);P.write('from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n');K.write_object_stub(P,N,C,B)
129
- K.report_add(C,I)
130
- if C not in{'os','sys','logging','gc'}:
131
- try:del N
132
- except(D,r):A.warning('could not del new_module')
133
- F.collect();return R
134
- def write_object_stub(L,fp,object_expr,obj_name,indent,in_class=0):
135
- 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()
136
- if M in L.problematic:A.warning('SKIPPING problematic module:{}'.format(M));return
137
- a,Q=L.get_obj_attributes(M)
138
- if Q:A.error(Q)
139
- for(E,H,I,b,d)in a:
140
- if E in['classmethod','staticmethod','BaseException',N]:continue
141
- if E[0].isdigit():A.warning('NameError: invalid name {}'.format(E));continue
142
- if I=="<class 'type'>"and P(D)<=A4*4:
143
- R=B;S=E.endswith(N)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit']
144
- if S:R=N
145
- C='\n{}class {}({}):\n'.format(D,E,R)
146
- if S:C+=D+' ...\n';J.write(C);continue
147
- 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)
148
- elif any(A in I for A in[A0,z,'closure']):
149
- T=V;U=B
150
- if O>0:U='self, '
151
- if W in I or W in H:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,T)
152
- else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,U,T)
153
- C+=D+' ...\n\n';J.write(C)
154
- elif I=="<class 'module'>":0
155
- elif I.startswith("<class '"):
156
- G=I[8:-2];C=B
157
- if G in(x,v,w,y,'bytearray','bytes'):C=X.format(D,E,H,G)
158
- elif G in(g,f,e):c={g:'{}',f:'[]',e:'()'};C=X.format(D,E,c[G],G)
159
- elif G in('object','set','frozenset','Pin',Y):
160
- if G==Y:G='Generator'
161
- C='{0}{1}: {2} ## = {4}\n'.format(D,E,G,I,H)
162
- else:
163
- G=V
164
- if K in H:H=H.split(K)[0]+Z
165
- if K in H:H=H.split(K)[0]+Z
166
- C='{0}{1}: {2} ## {3} = {4}\n'.format(D,E,G,I,H)
167
- J.write(C)
168
- else:J.write("# all other, type = '{0}'\n".format(I));J.write(D+E+' # type: Incomplete\n')
169
- @property
170
- def flat_fwid(self):
171
- A=self._fwid;B=' .()/\\:$'
172
- for C in B:A=A.replace(C,'_')
173
- return A
174
- def clean(C,path=E):
175
- if path is E:path=C.path
176
- A.info('Clean/remove files in folder: {}'.format(path))
177
- try:os.stat(path);F=os.listdir(path)
178
- except(D,I):return
179
- for G in F:
180
- B=i.format(path,G)
181
- try:os.remove(B)
182
- except D:
183
- try:C.clean(B);os.rmdir(B)
184
- except D:pass
185
- def report_start(B,filename=j):
186
- H='firmware';B._json_name=i.format(B.path,filename);B._json_first=R;X(B._json_name);A.info('Report file: {}'.format(B._json_name));F.collect()
187
- try:
188
- with M(B._json_name,h)as G:G.write('{');G.write(dumps({H:B.info})[1:-1]);G.write(k);G.write(dumps({t:{C:__version__},'stubtype':H})[1:-1]);G.write(k);G.write('"modules" :[\n')
189
- except D as I:A.error(A1);B._json_name=E;raise I
190
- def report_add(B,module_name,stub_file):
191
- if not B._json_name:raise s(A2)
192
- try:
193
- with M(B._json_name,'a')as C:
194
- if not B._json_first:C.write(k)
195
- else:B._json_first=H
196
- E='{{"module": "{}", "file": "{}"}}'.format(module_name,stub_file.replace('\\',G));C.write(E)
197
- except D:A.error(A1)
198
- def report_end(B):
199
- if not B._json_name:raise s(A2)
200
- with M(B._json_name,'a')as C:C.write('\n]}')
201
- A.info('Path: {}'.format(B.path))
202
- def X(path):
203
- B=E=0
204
- while B!=-1:
205
- B=path.find(G,E)
206
- if B!=-1:
207
- C=path[0]if B==0 else path[:B]
208
- try:I=os.stat(C)
209
- except D as F:
210
- if F.args[0]==A3:
211
- try:os.mkdir(C)
212
- except D as H:A.error('failed to create folder {}'.format(C));raise H
213
- E=B+1
214
- def Y(s):
215
- C=' on '
216
- if not s:return B
217
- s=s.split(C,1)[0]if C in s else s
218
- if s.startswith('v'):
219
- if not V in s:return B
220
- A=s.split(V)[1];return A
221
- if not W in s:return B
222
- A=s.split(W)[1].split(J)[1];return A
223
- def _info():
224
- a='ev3-pybricks';Z='pycom';X='pycopy';V='unix';S='win32';R='arch';P='cpu';M='ver';F='mpy';D='build'
225
- try:H=sys.implementation[0]
226
- except d:H=sys.implementation.name
227
- A=l({Q:H,C:B,D:B,M:B,K:sys.platform,U:'UNKNOWN',P:B,F:B,R:B})
228
- if A[K].startswith('pyb'):A[K]='stm32'
229
- elif A[K]==S:A[K]='windows'
230
- elif A[K]=='linux':A[K]=V
231
- try:A[C]=A6(sys.implementation.version)
232
- except I:pass
233
- try:J=sys.implementation._machine if'_machine'in N(sys.implementation)else os.uname().machine;A[U]=J;A[P]=J.split('with')[-1].strip();A[F]=sys.implementation._mpy if'_mpy'in N(sys.implementation)else sys.implementation.mpy if F in N(sys.implementation)else B
234
- except(I,T):pass
235
- A[U]=A7()
236
- try:
237
- if'uname'in N(os):
238
- A[D]=Y(os.uname()[3])
239
- if not A[D]:A[D]=Y(os.uname()[2])
240
- elif C in N(sys):A[D]=Y(sys.version)
241
- except(I,T,d):pass
242
- if A[C]==B and sys.platform not in(V,S):
243
- try:b=os.uname();A[C]=b.release
244
- except(T,I,d):pass
245
- for(c,e,f)in[(X,X,'const'),(Z,Z,'FAT'),(a,'pybricks.hubs','EV3Brick')]:
246
- try:g=__import__(e,E,E,f);A[Q]=c;del g;break
247
- except(O,r):pass
248
- if A[Q]==a:A['release']='2.0.0'
249
- if A[Q]==u:
250
- A[C]
251
- 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]
252
- if F in A and A[F]:
253
- G=int(A[F]);L=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10]
254
- if L:A[R]=L
255
- A[F]='v{}.{}'.format(G&255,G>>8&3)
256
- if A[D]and not A[C].endswith(W):A[C]=A[C]+W
257
- A[M]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A
258
- def A6(version):
259
- A=version;B=J.join([c(A)for A in A[:3]])
260
- if P(A)>3 and A[3]:B+=V+A[3]
261
- return B
262
- def A7():
263
- try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C))
264
- except O:A.warning('BOARDNAME not found');C=B
265
- return C
266
- def get_root():
267
- try:A=os.getcwd()
268
- except(D,I):A=J
269
- B=A
270
- for B in[A,'/sd','/flash',G,J]:
271
- try:C=os.stat(B);break
272
- except D:continue
273
- return B
274
- def Z(filename):
275
- try:
276
- if os.stat(filename)[0]>>14:return R
277
- return H
278
- except D:return H
279
- def m():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1)
280
- def read_path():
281
- path=B
282
- if P(sys.argv)==3:
283
- A=sys.argv[1].lower()
284
- if A in('--path','-p'):path=sys.argv[2]
285
- else:m()
286
- elif P(sys.argv)==2:m()
287
- return path
288
- def n():
289
- try:A=bytes('abc',encoding='utf8');B=n.__module__;return H
290
- except(o,I):return R
291
- a='modulelist.done'
292
- def A8(skip=0):
293
- for E in A5:
294
- B=E+'/modulelist.txt'
295
- if not Z(B):continue
296
- try:
297
- with M(B)as F:
298
- C=0
299
- while R:
300
- A=F.readline().strip()
301
- if not A:break
302
- if P(A)>0 and A[0]=='#':continue
303
- C+=1
304
- if C<skip:continue
305
- yield A
306
- break
307
- except D:pass
308
- def A9(done):
309
- with M(a,h)as A:A.write(c(done)+'\n')
310
- def AA():
311
- A=0
312
- try:
313
- with M(a)as B:A=int(B.readline().strip())
314
- except D:pass
315
- return A
92
+ "Generate stubs for modules in firmware"
93
+
94
+ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore
95
+ try:
96
+ if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore
97
+ raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed")
98
+ except AttributeError:
99
+ pass # Allow testing on CPython 3.11
100
+ self.info = _info()
101
+ log.info("Port: {}".format(self.info["port"]))
102
+ log.info("Board: {}".format(self.info["board"]))
103
+ gc.collect()
104
+ if firmware_id:
105
+ self._fwid = firmware_id.lower()
106
+ else:
107
+ if self.info["family"] == "micropython":
108
+ self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-")
109
+ else:
110
+ self._fwid = "{family}-v{version}-{port}".format(**self.info)
111
+ self._start_free = gc.mem_free() # type: ignore
112
+
113
+ if path:
114
+ if path.endswith("/"):
115
+ path = path[:-1]
116
+ else:
117
+ path = get_root()
118
+
119
+ self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/")
120
+ # log.debug(self.path)
121
+ try:
122
+ ensure_folder(path + "/")
123
+ except OSError:
124
+ log.error("error creating stub folder {}".format(path))
125
+ self.problematic = [
126
+ "upip",
127
+ "upysh",
128
+ "webrepl_setup",
129
+ "http_client",
130
+ "http_client_ssl",
131
+ "http_server",
132
+ "http_server_ssl",
133
+ ]
134
+ self.excluded = [
135
+ "webrepl",
136
+ "_webrepl",
137
+ "port_diag",
138
+ "example_sub_led.py",
139
+ "example_pub_button.py",
140
+ ]
141
+ # there is no option to discover modules from micropython, list is read from an external file.
142
+ self.modules = [] # type: list[str]
143
+ self._json_name = None
144
+ self._json_first = False
145
+
146
+ def get_obj_attributes(self, item_instance: object):
147
+ "extract information of the objects members and attributes"
148
+ # name_, repr_(value), type as text, item_instance
149
+ _result = []
150
+ _errors = []
151
+ # log.debug("get attributes {} {}".format(repr(item_instance), item_instance))
152
+ for name in dir(item_instance):
153
+ if name.startswith("__") and not name in self.modules:
154
+ continue
155
+ # log.debug("get attribute {}".format(name))
156
+ try:
157
+ val = getattr(item_instance, name)
158
+ # name , item_repr(value) , type as text, item_instance, order
159
+ # log.debug("attribute {}:{}".format(name, val))
160
+ try:
161
+ type_text = repr(type(val)).split("'")[1]
162
+ except IndexError:
163
+ type_text = ""
164
+ if type_text in {"int", "float", "str", "bool", "tuple", "list", "dict"}:
165
+ order = 1
166
+ elif type_text in {"function", "method"}:
167
+ order = 2
168
+ elif type_text in ("class"):
169
+ order = 3
170
+ else:
171
+ order = 4
172
+ _result.append((name, repr(val), repr(type(val)), val, order))
173
+ except AttributeError as e:
174
+ _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e))
175
+ except MemoryError as e:
176
+ print("MemoryError: {}".format(e))
177
+ sleep(1)
178
+ reset()
179
+
180
+ # remove internal __
181
+ # _result = sorted([i for i in _result if not (i[0].startswith("_"))], key=lambda x: x[4])
182
+ _result = sorted([i for i in _result if not (i[0].startswith("__"))], key=lambda x: x[4])
183
+ gc.collect()
184
+ return _result, _errors
185
+
186
+ def add_modules(self, modules):
187
+ "Add additional modules to be exported"
188
+ self.modules = sorted(set(self.modules) | set(modules))
189
+
190
+ def create_all_stubs(self):
191
+ "Create stubs for all configured modules"
192
+ log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid))
193
+ self.report_start()
194
+ gc.collect()
195
+ for module_name in self.modules:
196
+ self.create_one_stub(module_name)
197
+ self.report_end()
198
+ log.info("Finally done")
199
+
200
+ def create_one_stub(self, module_name: str):
201
+ if module_name in self.problematic:
202
+ log.warning("Skip module: {:<25} : Known problematic".format(module_name))
203
+ return False
204
+ if module_name in self.excluded:
205
+ log.warning("Skip module: {:<25} : Excluded".format(module_name))
206
+ return False
207
+
208
+ file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/"))
209
+ gc.collect()
210
+ result = False
211
+ try:
212
+ result = self.create_module_stub(module_name, file_name)
213
+ except OSError:
214
+ return False
215
+ gc.collect()
216
+ return result
217
+
218
+ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: # type: ignore
219
+ """Create a Stub of a single python module
220
+
221
+ Args:
222
+ - module_name (str): name of the module to document. This module will be imported.
223
+ - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name.
224
+ """
225
+ if file_name is None:
226
+ fname = module_name.replace(".", "_") + ".pyi"
227
+ file_name = self.path + "/" + fname
228
+ else:
229
+ fname = file_name.split("/")[-1]
230
+
231
+ if "/" in module_name:
232
+ # for nested modules
233
+ module_name = module_name.replace("/", ".")
234
+
235
+ # import the module (as new_module) to examine it
236
+ new_module = None
237
+ try:
238
+ new_module = __import__(module_name, None, None, ("*"))
239
+ m1 = gc.mem_free() # type: ignore
240
+ log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1))
241
+
242
+ except ImportError:
243
+ # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
244
+ return False
245
+
246
+ # Start a new file
247
+ ensure_folder(file_name)
248
+ with open(file_name, "w") as fp:
249
+ # todo: improve header
250
+ info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}")
251
+ s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__)
252
+ fp.write(s)
253
+ fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n")
254
+ self.write_object_stub(fp, new_module, module_name, "")
255
+
256
+ self.report_add(module_name, file_name)
257
+
258
+ if module_name not in {"os", "sys", "logging", "gc"}:
259
+ # try to unload the module unless we use it
260
+ try:
261
+ del new_module
262
+ except (OSError, KeyError): # lgtm [py/unreachable-statement]
263
+ log.warning("could not del new_module")
264
+ # do not try to delete from sys.modules - most times it does not work anyway
265
+ gc.collect()
266
+ return True
267
+
268
+ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0):
269
+ "Write a module/object stub to an open file. Can be called recursive."
270
+ gc.collect()
271
+ if object_expr in self.problematic:
272
+ log.warning("SKIPPING problematic module:{}".format(object_expr))
273
+ return
274
+
275
+ # # log.debug("DUMP : {}".format(object_expr))
276
+ items, errors = self.get_obj_attributes(object_expr)
277
+
278
+ if errors:
279
+ log.error(errors)
280
+
281
+ for item_name, item_repr, item_type_txt, item_instance, _ in items:
282
+ # name_, repr_(value), type as text, item_instance, order
283
+ if item_name in ["classmethod", "staticmethod", "BaseException", "Exception"]:
284
+ # do not create stubs for these primitives
285
+ continue
286
+ if item_name[0].isdigit():
287
+ log.warning("NameError: invalid name {}".format(item_name))
288
+ continue
289
+ # Class expansion only on first 3 levels (bit of a hack)
290
+ if (
291
+ item_type_txt == "<class 'type'>"
292
+ and len(indent) <= _MAX_CLASS_LEVEL * 4
293
+ # and not obj_name.endswith(".Pin")
294
+ # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms
295
+ ):
296
+ # log.debug("{0}class {1}:".format(indent, item_name))
297
+ superclass = ""
298
+ is_exception = (
299
+ item_name.endswith("Exception")
300
+ or item_name.endswith("Error")
301
+ or item_name
302
+ in [
303
+ "KeyboardInterrupt",
304
+ "StopIteration",
305
+ "SystemExit",
306
+ ]
307
+ )
308
+ if is_exception:
309
+ superclass = "Exception"
310
+ s = "\n{}class {}({}):\n".format(indent, item_name, superclass)
311
+ # s += indent + " ''\n"
312
+ if is_exception:
313
+ s += indent + " ...\n"
314
+ fp.write(s)
315
+ continue
316
+ # write classdef
317
+ fp.write(s)
318
+ # first write the class literals and methods
319
+ # log.debug("# recursion over class {0}".format(item_name))
320
+ self.write_object_stub(
321
+ fp,
322
+ item_instance,
323
+ "{0}.{1}".format(obj_name, item_name),
324
+ indent + " ",
325
+ in_class + 1,
326
+ )
327
+ # end with the __init__ method to make sure that the literals are defined
328
+ # Add __init__
329
+ s = indent + " def __init__(self, *argv, **kwargs) -> None:\n"
330
+ s += indent + " ...\n\n"
331
+ fp.write(s)
332
+ elif any(word in item_type_txt for word in ["method", "function", "closure"]):
333
+ # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name))
334
+ # module Function or class method
335
+ # will accept any number of params
336
+ # return type Any/Incomplete
337
+ ret = "Incomplete"
338
+ first = ""
339
+ # Self parameter only on class methods/functions
340
+ if in_class > 0:
341
+ first = "self, "
342
+ # class method - add function decoration
343
+ if "bound_method" in item_type_txt or "bound_method" in item_repr:
344
+ s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret)
345
+ else:
346
+ s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret)
347
+ s += indent + " ...\n\n"
348
+ fp.write(s)
349
+ # log.debug("\n" + s)
350
+ elif item_type_txt == "<class 'module'>":
351
+ # Skip imported modules
352
+ # fp.write("# import {}\n".format(item_name))
353
+ pass
354
+
355
+ elif item_type_txt.startswith("<class '"):
356
+ t = item_type_txt[8:-2]
357
+ s = ""
358
+
359
+ if t in ("str", "int", "float", "bool", "bytearray", "bytes"):
360
+ # known type: use actual value
361
+ # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t)
362
+ s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t)
363
+ elif t in ("dict", "list", "tuple"):
364
+ # dict, list , tuple: use empty value
365
+ ev = {"dict": "{}", "list": "[]", "tuple": "()"}
366
+ # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t)
367
+ s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t)
368
+ else:
369
+ # something else
370
+ if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO"
371
+ # https://docs.python.org/3/tutorial/classes.html#item_instance-objects
372
+ # use these types for the attribute
373
+ if t == "generator":
374
+ t = "Generator"
375
+ s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
376
+ else:
377
+ # Requires Python 3.6 syntax, which is OK for the stubs/pyi
378
+ t = "Incomplete"
379
+ if " at " in item_repr:
380
+ item_repr = item_repr.split(" at ")[0] + " at ...>"
381
+ if " at " in item_repr:
382
+ item_repr = item_repr.split(" at ")[0] + " at ...>"
383
+ s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr)
384
+ fp.write(s)
385
+ # log.debug("\n" + s)
386
+ else:
387
+ # keep only the name
388
+ # log.debug("# all other, type = '{0}'".format(item_type_txt))
389
+ fp.write("# all other, type = '{0}'\n".format(item_type_txt))
390
+
391
+ fp.write(indent + item_name + " # type: Incomplete\n")
392
+
393
+ # del items
394
+ # del errors
395
+ # try:
396
+ # del item_name, item_repr, item_type_txt, item_instance # type: ignore
397
+ # except (OSError, KeyError, NameError):
398
+ # pass
399
+
400
+ @property
401
+ def flat_fwid(self):
402
+ "Turn _fwid from 'v1.2.3' into '1_2_3' to be used in filename"
403
+ s = self._fwid
404
+ # path name restrictions
405
+ chars = " .()/\\:$"
406
+ for c in chars:
407
+ s = s.replace(c, "_")
408
+ return s
409
+
410
+ def clean(self, path: str = None): # type: ignore
411
+ "Remove all files from the stub folder"
412
+ if path is None:
413
+ path = self.path
414
+ log.info("Clean/remove files in folder: {}".format(path))
415
+ try:
416
+ os.stat(path) # TEMP workaround mpremote listdir bug -
417
+ items = os.listdir(path)
418
+ except (OSError, AttributeError):
419
+ # os.listdir fails on unix
420
+ return
421
+ for fn in items:
422
+ item = "{}/{}".format(path, fn)
423
+ try:
424
+ os.remove(item)
425
+ except OSError:
426
+ try: # folder
427
+ self.clean(item)
428
+ os.rmdir(item)
429
+ except OSError:
430
+ pass
431
+
432
+ def report_start(self, filename: str = "modules.json"):
433
+ """Start a report of the modules that have been stubbed
434
+ "create json with list of exported modules"""
435
+ self._json_name = "{}/{}".format(self.path, filename)
436
+ self._json_first = True
437
+ ensure_folder(self._json_name)
438
+ log.info("Report file: {}".format(self._json_name))
439
+ gc.collect()
440
+ try:
441
+ # write json by node to reduce memory requirements
442
+ with open(self._json_name, "w") as f:
443
+ f.write("{")
444
+ f.write(dumps({"firmware": self.info})[1:-1])
445
+ f.write(",\n")
446
+ f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1])
447
+ f.write(",\n")
448
+ f.write('"modules" :[\n')
449
+
450
+ except OSError as e:
451
+ log.error("Failed to create the report.")
452
+ self._json_name = None
453
+ raise e
454
+
455
+ def report_add(self, module_name: str, stub_file: str):
456
+ "Add a module to the report"
457
+ # write json by node to reduce memory requirements
458
+ if not self._json_name:
459
+ raise Exception("No report file")
460
+ try:
461
+ with open(self._json_name, "a") as f:
462
+ if not self._json_first:
463
+ f.write(",\n")
464
+ else:
465
+ self._json_first = False
466
+ line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/"))
467
+ f.write(line)
468
+
469
+ except OSError:
470
+ log.error("Failed to create the report.")
471
+
472
+ def report_end(self):
473
+ if not self._json_name:
474
+ raise Exception("No report file")
475
+ with open(self._json_name, "a") as f:
476
+ f.write("\n]}")
477
+ # is used as sucess indicator
478
+ log.info("Path: {}".format(self.path))
479
+
480
+
481
+ def ensure_folder(path: str):
482
+ "Create nested folders if needed"
483
+ i = start = 0
484
+ while i != -1:
485
+ i = path.find("/", start)
486
+ if i != -1:
487
+ p = path[0] if i == 0 else path[:i]
488
+ # p = partial folder
489
+ try:
490
+ _ = os.stat(p)
491
+ except OSError as e:
492
+ # folder does not exist
493
+ if e.args[0] == ENOENT:
494
+ try:
495
+ os.mkdir(p)
496
+ except OSError as e2:
497
+ log.error("failed to create folder {}".format(p))
498
+ raise e2
499
+ # next level deep
500
+ start = i + 1
501
+
502
+
503
+ def _build(s):
504
+ # extract build from sys.version or os.uname().version if available
505
+ # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f'
506
+ # sys.implementation.version: 'v1.13-103-gb137d064e'
507
+ if not s:
508
+ return ""
509
+ s = s.split(" on ", 1)[0] if " on " in s else s
510
+ if s.startswith("v"):
511
+ if not "-" in s:
512
+ return ""
513
+ b = s.split("-")[1]
514
+ return b
515
+ if not "-preview" in s:
516
+ return ""
517
+ b = s.split("-preview")[1].split(".")[1]
518
+ return b
519
+
520
+
521
+ def _info(): # type:() -> dict[str, str]
522
+ try:
523
+ fam = sys.implementation[0] # type: ignore
524
+ except TypeError:
525
+ # testing on CPython 3.11
526
+ fam = sys.implementation.name
527
+
528
+ info = OrderedDict(
529
+ {
530
+ "family": fam,
531
+ "version": "",
532
+ "build": "",
533
+ "ver": "",
534
+ "port": sys.platform, # port: esp32 / win32 / linux / stm32
535
+ "board": "UNKNOWN",
536
+ "cpu": "",
537
+ "mpy": "",
538
+ "arch": "",
539
+ }
540
+ )
541
+ # change port names to be consistent with the repo
542
+ if info["port"].startswith("pyb"):
543
+ info["port"] = "stm32"
544
+ elif info["port"] == "win32":
545
+ info["port"] = "windows"
546
+ elif info["port"] == "linux":
547
+ info["port"] = "unix"
548
+ try:
549
+ info["version"] = version_str(sys.implementation.version) # type: ignore
550
+ except AttributeError:
551
+ pass
552
+ try:
553
+ _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore
554
+ # info["board"] = "with".join(_machine.split("with")[:-1]).strip()
555
+ info["board"] = _machine
556
+ info["cpu"] = _machine.split("with")[-1].strip()
557
+ info["mpy"] = (
558
+ sys.implementation._mpy # type: ignore
559
+ if "_mpy" in dir(sys.implementation)
560
+ else sys.implementation.mpy
561
+ if "mpy" in dir(sys.implementation)
562
+ else "" # type: ignore
563
+ )
564
+ except (AttributeError, IndexError):
565
+ pass
566
+ info["board"] = get_boardname()
567
+
568
+ try:
569
+ if "uname" in dir(os): # old
570
+ # extract build from uname().version if available
571
+ info["build"] = _build(os.uname()[3]) # type: ignore
572
+ if not info["build"]:
573
+ # extract build from uname().release if available
574
+ info["build"] = _build(os.uname()[2]) # type: ignore
575
+ elif "version" in dir(sys): # new
576
+ # extract build from sys.version if available
577
+ info["build"] = _build(sys.version)
578
+ except (AttributeError, IndexError, TypeError):
579
+ pass
580
+ # avoid build hashes
581
+ # if info["build"] and len(info["build"]) > 5:
582
+ # info["build"] = ""
583
+
584
+ if info["version"] == "" and sys.platform not in ("unix", "win32"):
585
+ try:
586
+ u = os.uname() # type: ignore
587
+ info["version"] = u.release
588
+ except (IndexError, AttributeError, TypeError):
589
+ pass
590
+ # detect families
591
+ for fam_name, mod_name, mod_thing in [
592
+ ("pycopy", "pycopy", "const"),
593
+ ("pycom", "pycom", "FAT"),
594
+ ("ev3-pybricks", "pybricks.hubs", "EV3Brick"),
595
+ ]:
596
+ try:
597
+ _t = __import__(mod_name, None, None, (mod_thing))
598
+ info["family"] = fam_name
599
+ del _t
600
+ break
601
+ except (ImportError, KeyError):
602
+ pass
603
+
604
+ if info["family"] == "ev3-pybricks":
605
+ info["release"] = "2.0.0"
606
+
607
+ if info["family"] == "micropython":
608
+ info["version"]
609
+ if (
610
+ info["version"]
611
+ and info["version"].endswith(".0")
612
+ and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.1 do not have a micro .0
613
+ and info["version"] <= "1.19.9"
614
+ ):
615
+ # versions from 1.10.0 to 1.20.1 do not have a micro .0
616
+ info["version"] = info["version"][:-2]
617
+
618
+ # spell-checker: disable
619
+ if "mpy" in info and info["mpy"]: # mpy on some v1.11+ builds
620
+ sys_mpy = int(info["mpy"])
621
+ # .mpy architecture
622
+ arch = [
623
+ None,
624
+ "x86",
625
+ "x64",
626
+ "armv6",
627
+ "armv6m",
628
+ "armv7m",
629
+ "armv7em",
630
+ "armv7emsp",
631
+ "armv7emdp",
632
+ "xtensa",
633
+ "xtensawin",
634
+ ][sys_mpy >> 10]
635
+ if arch:
636
+ info["arch"] = arch
637
+ # .mpy version.minor
638
+ info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3)
639
+ if info["build"] and not info["version"].endswith("-preview"):
640
+ info["version"] = info["version"] + "-preview"
641
+ # simple to use version[-build] string
642
+ info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}"
643
+
644
+ return info
645
+
646
+
647
+ def version_str(version: tuple): # -> str:
648
+ v_str = ".".join([str(n) for n in version[:3]])
649
+ if len(version) > 3 and version[3]:
650
+ v_str += "-" + version[3]
651
+ return v_str
652
+
653
+
654
+ def get_boardname() -> str:
655
+ "Read the board name from the boardname.py file that may have been created upfront"
656
+ try:
657
+ from boardname import BOARDNAME # type: ignore
658
+
659
+ log.info("Found BOARDNAME: {}".format(BOARDNAME))
660
+ except ImportError:
661
+ log.warning("BOARDNAME not found")
662
+ BOARDNAME = ""
663
+ return BOARDNAME
664
+
665
+
666
+ def get_root() -> str: # sourcery skip: use-assigned-variable
667
+ "Determine the root folder of the device"
668
+ try:
669
+ c = os.getcwd()
670
+ except (OSError, AttributeError):
671
+ # unix port
672
+ c = "."
673
+ r = c
674
+ for r in [c, "/sd", "/flash", "/", "."]:
675
+ try:
676
+ _ = os.stat(r)
677
+ break
678
+ except OSError:
679
+ continue
680
+ return r
681
+
682
+
683
+ def file_exists(filename: str):
684
+ try:
685
+ if os.stat(filename)[0] >> 14:
686
+ return True
687
+ return False
688
+ except OSError:
689
+ return False
690
+
691
+
692
+ def show_help():
693
+ print("-p, --path path to store the stubs in, defaults to '.'")
694
+ sys.exit(1)
695
+
696
+
697
+ def read_path() -> str:
698
+ "get --path from cmdline. [unix/win]"
699
+ path = ""
700
+ if len(sys.argv) == 3:
701
+ cmd = (sys.argv[1]).lower()
702
+ if cmd in ("--path", "-p"):
703
+ path = sys.argv[2]
704
+ else:
705
+ show_help()
706
+ elif len(sys.argv) == 2:
707
+ show_help()
708
+ return path
709
+
710
+
711
+ def is_micropython() -> bool:
712
+ "runtime test to determine full or micropython"
713
+ # pylint: disable=unused-variable,eval-used
714
+ try:
715
+ # either test should fail on micropython
716
+
717
+ # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented
718
+ # Micropython: NotImplementedError
719
+ b = bytes("abc", encoding="utf8") # type: ignore # lgtm [py/unused-local-variable]
720
+
721
+ # c) https://docs.micropython.org/en/latest/genrst/core_language.html#function-objects-do-not-have-the-module-attribute
722
+ # Micropython: AttributeError
723
+ c = is_micropython.__module__ # type: ignore # lgtm [py/unused-local-variable]
724
+ return False
725
+ except (NotImplementedError, AttributeError):
726
+ return True
727
+
728
+
729
+ SKIP_FILE = "modulelist.done"
730
+
731
+
732
+ def get_modules(skip=0):
733
+ # new
734
+ for p in LIBS:
735
+ fname = p + "/modulelist.txt"
736
+ if not file_exists(fname):
737
+ continue
738
+ try:
739
+ with open(fname) as f:
740
+ i = 0
741
+ while True:
742
+ line = f.readline().strip()
743
+ if not line:
744
+ break
745
+ if len(line) > 0 and line[0] == "#":
746
+ continue
747
+ i += 1
748
+ if i < skip:
749
+ continue
750
+ yield line
751
+ break
752
+ except OSError:
753
+ pass
754
+
755
+
756
+ def write_skip(done):
757
+ # write count of modules already processed to file
758
+ with open(SKIP_FILE, "w") as f:
759
+ f.write(str(done) + "\n")
760
+
761
+
762
+ def read_skip():
763
+ # read count of modules already processed from file
764
+ done = 0
765
+ try:
766
+ with open(SKIP_FILE) as f:
767
+ done = int(f.readline().strip())
768
+ except OSError:
769
+ pass
770
+ return done
771
+
772
+
316
773
  def main():
317
- import machine as D;C=Z(a)
318
- if C:A.info('Continue from last run')
319
- else:A.info('Starting new run')
320
- stubber=Stubber(path=read_path());B=0
321
- if not C:stubber.clean();stubber.report_start(j)
322
- else:B=AA();stubber._json_name=i.format(stubber.path,j)
323
- for E in A8(B):
324
- try:stubber.create_one_stub(E)
325
- except p:D.reset()
326
- F.collect();B+=1;A9(B)
327
- S('All modules have been processed, Finalizing report');stubber.report_end()
328
- if __name__=='__main__'or n():
329
- if not Z('no_auto_stubber.txt'):
330
- try:F.threshold(4*1024);F.enable()
331
- except BaseException:pass
332
- main()
774
+ import machine # type: ignore
775
+
776
+ was_running = file_exists(SKIP_FILE)
777
+ if was_running:
778
+ log.info("Continue from last run")
779
+ else:
780
+ log.info("Starting new run")
781
+ # try:
782
+ # f = open("modulelist.done", "r+b")
783
+ # was_running = True
784
+ # print("Continue from last run")
785
+ # except OSError:
786
+ # f = open("modulelist.done", "w+b")
787
+ # was_running = False
788
+ stubber = Stubber(path=read_path())
789
+
790
+ # f_name = "{}/{}".format(stubber.path, "modules.json")
791
+ skip = 0
792
+ if not was_running:
793
+ # Only clean folder if this is a first run
794
+ stubber.clean()
795
+ stubber.report_start("modules.json")
796
+ else:
797
+ skip = read_skip()
798
+ stubber._json_name = "{}/{}".format(stubber.path, "modules.json")
799
+
800
+ for modulename in get_modules(skip):
801
+ # ------------------------------------
802
+ # do epic shit
803
+ # but sometimes things fail / run out of memory and reboot
804
+ try:
805
+ stubber.create_one_stub(modulename)
806
+ except MemoryError:
807
+ # RESET AND HOPE THAT IN THE NEXT CYCLE WE PROGRESS FURTHER
808
+ machine.reset()
809
+ # -------------------------------------
810
+ gc.collect()
811
+ # modules_done[modulename] = str(stubber._report[-1] if ok else "failed")
812
+ # with open("modulelist.done", "a") as f:
813
+ # f.write("{}={}\n".format(modulename, "ok" if ok else "failed"))
814
+ skip += 1
815
+ write_skip(skip)
816
+
817
+ print("All modules have been processed, Finalizing report")
818
+ stubber.report_end()
819
+
820
+
821
+ if __name__ == "__main__" or is_micropython():
822
+ if not file_exists("no_auto_stubber.txt"):
823
+ try:
824
+ gc.threshold(4 * 1024) # type: ignore
825
+ gc.enable()
826
+ except BaseException:
827
+ pass
828
+ main()