2525 from ucollections import OrderedDict # type: ignore
2626
2727__version__ = "v1.26.1"
28- ENOENT = 2 # on most ports
29- ENOMESSAGE = 44 # on pyscript
28+ ENOENT = 2 # on most ports
29+ ENOMESSAGE = 44 # on pyscript
3030_MAX_CLASS_LEVEL = 2 # Max class nesting
3131LIBS = ["lib" , "/lib" , "/sd/lib" , "/flash/lib" , "." ]
3232
@@ -154,11 +154,7 @@ def get_obj_attributes(self, item_instance: object):
154154 order = 4
155155 _result .append ((name , repr (val ), repr (type (val )), val , order ))
156156 except AttributeError as e :
157- _errors .append (
158- "Couldn't get attribute '{}' from object '{}', Err: {}" .format (
159- name , item_instance , e
160- )
161- )
157+ _errors .append ("Couldn't get attribute '{}' from object '{}', Err: {}" .format (name , item_instance , e ))
162158 except MemoryError as e :
163159 print ("MemoryError: {}" .format (e ))
164160 sleep (1 )
@@ -224,9 +220,7 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool:
224220 try :
225221 new_module = __import__ (module_name , None , None , ("*" ))
226222 m1 = gc .mem_free () # type: ignore
227- log .info (
228- "Stub module: {:<25} to file: {:<70} mem:{:>5}" .format (module_name , fname , m1 )
229- )
223+ log .info ("Stub module: {:<25} to file: {:<70} mem:{:>5}" .format (module_name , fname , m1 ))
230224
231225 except ImportError :
232226 # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found."))
@@ -237,13 +231,9 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool:
237231 ensure_folder (file_name )
238232 with open (file_name , "w" ) as fp :
239233 info_ = str (self .info ).replace ("OrderedDict(" , "" ).replace ("})" , "}" )
240- s = '"""\n Module: \' {0}\' on {1}\n """\n # MCU: {2}\n # Stubber: {3}\n ' .format (
241- module_name , self ._fwid , info_ , __version__
242- )
234+ s = '"""\n Module: \' {0}\' on {1}\n """\n # MCU: {2}\n # Stubber: {3}\n ' .format (module_name , self ._fwid , info_ , __version__ )
243235 fp .write (s )
244- fp .write (
245- "from __future__ import annotations\n from typing import Any, Final, Generator\n from _typeshed import Incomplete\n \n "
246- )
236+ fp .write ("from __future__ import annotations\n from typing import Any, Final, Generator\n from _typeshed import Incomplete\n \n " )
247237 self .write_object_stub (fp , new_module , module_name , "" )
248238
249239 self .report_add (module_name , file_name )
@@ -258,9 +248,7 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool:
258248 gc .collect ()
259249 return True
260250
261- def write_object_stub (
262- self , fp , object_expr : object , obj_name : str , indent : str , in_class : int = 0
263- ):
251+ def write_object_stub (self , fp , object_expr : object , obj_name : str , indent : str , in_class : int = 0 ):
264252 "Write a module/object stub to an open file. Can be called recursive."
265253 gc .collect ()
266254 if object_expr in self .problematic :
@@ -336,13 +324,9 @@ def write_object_stub(
336324 first = "self, "
337325 # class method - add function decoration
338326 if "bound_method" in item_type_txt or "bound_method" in item_repr :
339- s = "{}@classmethod\n " .format (
340- indent
341- ) + "{}def {}(cls, *args, **kwargs) -> {}:\n " .format (indent , item_name , ret )
327+ s = "{}@classmethod\n " .format (indent ) + "{}def {}(cls, *args, **kwargs) -> {}:\n " .format (indent , item_name , ret )
342328 else :
343- s = "{}def {}({}*args, **kwargs) -> {}:\n " .format (
344- indent , item_name , first , ret
345- )
329+ s = "{}def {}({}*args, **kwargs) -> {}:\n " .format (indent , item_name , first , ret )
346330 s += indent + " ...\n \n "
347331 fp .write (s )
348332 # log.debug("\n" + s)
@@ -371,9 +355,7 @@ def write_object_stub(
371355 if t in ("object" , "set" , "frozenset" , "Pin" ): # "FileIO"
372356 # https://docs.python.org/3/tutorial/classes.html#item_instance-objects
373357 # use these types for the attribute
374- s = "{0}{1}: {2} ## = {4}\n " .format (
375- indent , item_name , t , item_type_txt , item_repr
376- )
358+ s = "{0}{1}: {2} ## = {4}\n " .format (indent , item_name , t , item_type_txt , item_repr )
377359 elif t == "generator" :
378360 # either a normal or async Generator function
379361 t = "Generator"
@@ -387,9 +369,7 @@ def write_object_stub(
387369 item_repr = item_repr .split (" at " )[0 ] + " at ...>"
388370 if " at " in item_repr :
389371 item_repr = item_repr .split (" at " )[0 ] + " at ...>"
390- s = "{0}{1}: {2} ## {3} = {4}\n " .format (
391- indent , item_name , t , item_type_txt , item_repr
392- )
372+ s = "{0}{1}: {2} ## {3} = {4}\n " .format (indent , item_name , t , item_type_txt , item_repr )
393373 fp .write (s )
394374 # log.debug("\n" + s)
395375 else :
@@ -472,9 +452,7 @@ def report_add(self, module_name: str, stub_file: str):
472452 f .write (",\n " )
473453 else :
474454 self ._json_first = False
475- line = '{{"module": "{}", "file": "{}"}}' .format (
476- module_name , stub_file .replace ("\\ " , "/" )
477- )
455+ line = '{{"module": "{}", "file": "{}"}}' .format (module_name , stub_file .replace ("\\ " , "/" ))
478456 f .write (line )
479457
480458 except OSError :
@@ -501,7 +479,7 @@ def ensure_folder(path: str):
501479 _ = os .stat (p )
502480 except OSError as e :
503481 # folder does not exist
504- if e .args [0 ] in [ENOENT , ENOMESSAGE ] :
482+ if e .args [0 ] in [ENOENT , ENOMESSAGE ]:
505483 try :
506484 log .debug ("Create folder {}" .format (p ))
507485 os .mkdir (p )
@@ -512,7 +490,6 @@ def ensure_folder(path: str):
512490 start = i + 1
513491
514492
515-
516493def _build (s ):
517494 # extract build from sys.version or os.uname().version if available
518495 # sys.version: 'MicroPython v1.24.0-preview.6.g3d0b6276f'
@@ -531,7 +508,8 @@ def _build(s):
531508 return b
532509
533510
534- def _info (): # type:() -> dict[str, str]
511+ def _get_base_system_info () -> OrderedDict [str , str ]:
512+ """Get basic system implementation details."""
535513 try :
536514 fam = sys .implementation [0 ] # type: ignore
537515 except TypeError :
@@ -553,21 +531,31 @@ def _info(): # type:() -> dict[str, str]
553531 "arch" : "" ,
554532 }
555533 )
556- # change port names to be consistent with the repo
534+ return info
535+
536+
537+ def _normalize_port_info (info : OrderedDict [str , str ]) -> None :
538+ """Normalize port names to be consistent with the repo."""
557539 if info ["port" ].startswith ("pyb" ):
558540 info ["port" ] = "stm32"
559541 elif info ["port" ] == "win32" :
560542 info ["port" ] = "windows"
561543 elif info ["port" ] == "linux" :
562544 info ["port" ] = "unix"
545+
546+
547+ def _extract_version_info (info : OrderedDict [str , str ]) -> None :
548+ """Extract version information from sys.implementation."""
563549 try :
564550 info ["version" ] = version_str (sys .implementation .version ) # type: ignore
565551 except AttributeError :
566552 pass
553+
554+
555+ def _extract_hardware_info (info : OrderedDict [str , str ]) -> None :
556+ """Extract board, CPU, and machine details."""
567557 try :
568- _machine = (
569- sys .implementation ._machine if "_machine" in dir (sys .implementation ) else os .uname ().machine # type: ignore
570- )
558+ _machine = sys .implementation ._machine if "_machine" in dir (sys .implementation ) else os .uname ().machine # type: ignore
571559 info ["board" ] = _machine .strip ()
572560 si_build = sys .implementation ._build if "_build" in dir (sys .implementation ) else ""
573561 if si_build :
@@ -582,9 +570,13 @@ def _info(): # type:() -> dict[str, str]
582570 )
583571 except (AttributeError , IndexError ):
584572 pass
573+
585574 if not info ["board_id" ]:
586575 get_boardname (info )
587576
577+
578+ def _extract_build_info (info : OrderedDict [str , str ]) -> None :
579+ """Extract build information from various system sources."""
588580 try :
589581 if "uname" in dir (os ): # old
590582 # extract build from uname().version if available
@@ -597,17 +589,18 @@ def _info(): # type:() -> dict[str, str]
597589 info ["build" ] = _build (sys .version )
598590 except (AttributeError , IndexError , TypeError ):
599591 pass
600- # avoid build hashes
601- # if info["build"] and len(info["build"]) > 5:
602- # info["build"] = ""
603592
593+ # Fallback version detection for specific platforms
604594 if info ["version" ] == "" and sys .platform not in ("unix" , "win32" ):
605595 try :
606596 u = os .uname () # type: ignore
607597 info ["version" ] = u .release
608598 except (IndexError , AttributeError , TypeError ):
609599 pass
610- # detect families
600+
601+
602+ def _detect_firmware_family (info : OrderedDict [str , str ]) -> None :
603+ """Detect special firmware families (pycopy, pycom, ev3-pybricks)."""
611604 for fam_name , mod_name , mod_thing in [
612605 ("pycopy" , "pycopy" , "const" ),
613606 ("pycom" , "pycom" , "FAT" ),
@@ -624,18 +617,22 @@ def _info(): # type:() -> dict[str, str]
624617 if info ["family" ] == "ev3-pybricks" :
625618 info ["release" ] = "2.0.0"
626619
620+
621+ def _process_micropython_version (info : OrderedDict [str , str ]) -> None :
622+ """Process MicroPython-specific version formatting."""
627623 if info ["family" ] == "micropython" :
628- info ["version" ]
629624 if (
630625 info ["version" ]
631626 and info ["version" ].endswith (".0" )
632- and info ["version" ]
633- >= "1.10.0" # versions from 1.10.0 to 1.24.0 do not have a micro .0
627+ and info ["version" ] >= "1.10.0" # versions from 1.10.0 to 1.24.0 do not have a micro .0
634628 and info ["version" ] <= "1.19.9"
635629 ):
636630 # versions from 1.10.0 to 1.24.0 do not have a micro .0
637631 info ["version" ] = info ["version" ][:- 2 ]
638632
633+
634+ def _process_mpy_info (info : OrderedDict [str , str ]) -> None :
635+ """Process MPY architecture and version information."""
639636 # spell-checker: disable
640637 if "mpy" in info and info ["mpy" ]: # mpy on some v1.11+ builds
641638 sys_mpy = int (info ["mpy" ])
@@ -661,11 +658,36 @@ def _info(): # type:() -> dict[str, str]
661658 info ["arch" ] = "unknown"
662659 # .mpy version.minor
663660 info ["mpy" ] = "v{}.{}" .format (sys_mpy & 0xFF , sys_mpy >> 8 & 3 )
661+
662+
663+ def _format_version_strings (info : OrderedDict [str , str ]) -> None :
664+ """Handle final version string formatting."""
664665 if info ["build" ] and not info ["version" ].endswith ("-preview" ):
665666 info ["version" ] = info ["version" ] + "-preview"
666667 # simple to use version[-build] string
667668 info ["ver" ] = f"{ info ['version' ]} -{ info ['build' ]} " if info ["build" ] else f"{ info ['version' ]} "
668669
670+
671+ def _info (): # type:() -> dict[str, str]
672+ """
673+ Gather comprehensive system information for MicroPython stubbing.
674+
675+ Returns a dictionary containing family, version, port, board, and other
676+ system details needed for stub generation.
677+ """
678+ # Get base system information
679+ info = _get_base_system_info ()
680+
681+ # Apply transformations and gather additional info
682+ _normalize_port_info (info )
683+ _extract_version_info (info )
684+ _extract_hardware_info (info )
685+ _extract_build_info (info )
686+ _detect_firmware_family (info )
687+ _process_micropython_version (info )
688+ _process_mpy_info (info )
689+ _format_version_strings (info )
690+
669691 return info
670692
671693
@@ -743,11 +765,11 @@ def is_micropython() -> bool:
743765
744766 # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented
745767 # Micropython: NotImplementedError
746- b = bytes ("abc" , encoding = "utf8" ) # type: ignore
768+ b = bytes ("abc" , encoding = "utf8" ) # type: ignore
747769
748770 # c) https://docs.micropython.org/en/latest/genrst/core_language.html#function-objects-do-not-have-the-module-attribute
749771 # Micropython: AttributeError
750- c = is_micropython .__module__ # type: ignore
772+ c = is_micropython .__module__ # type: ignore
751773 return False
752774 except (NotImplementedError , AttributeError ):
753775 return True
0 commit comments