diff --git a/.env b/.env index 60a4812aa..3661fc02e 100644 --- a/.env +++ b/.env @@ -1,8 +1,7 @@ # Python .env file .. -# Declutter Store all __pycache__ files in the .py_cache folder -PYTHONPYCACHEPREFIX = ".py_cache" - +JUPYTER_PLATFORM_DIRS=1 +PYDEVD_DISABLE_FILE_VALIDATION=1 # Allow tests to find sources PYTHONPATH = src @@ -10,4 +9,8 @@ PYTHONPATH = src PYDEVD_WARN_EVALUATION_TIMEOUT = 20 # stop nagging for updates -PIP_DISABLE_PIP_VERSION_CHECK=1 \ No newline at end of file +PIP_DISABLE_PIP_VERSION_CHECK=1 + +# stop neggin during debug +JUPYTER_PLATFORM_DIRS=1 +PYDEVD_DISABLE_FILE_VALIDATION=1 \ No newline at end of file diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 8226860e2..f4a71fc97 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -7,6 +7,9 @@ name: pytest on: workflow_dispatch: + pull_request: + # branches: [main] + # push: # paths: # - 'board/**' @@ -17,8 +20,6 @@ on: # - "poetry.lock" # - "pyrightconfig.json" - pull_request: - branches: [main] jobs: run_tests: diff --git a/.gitignore b/.gitignore index cd1548488..fe4b87a43 100644 --- a/.gitignore +++ b/.gitignore @@ -60,9 +60,11 @@ snippets/*/typings **/typings_* typings_test typings +.vscode/Pico-W-Stub empty **/*_lock.file # do not check in any downloaded firmwares **/firmware tmp_minified.py +src/stubber/board/board_info.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 1a452b4f9..e1799599d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,8 +5,8 @@ "version": "0.2.0", "configurations": [ { - "name": "dgb pytest --collect-only", - "type": "python", + "name": "Pytest --collect-only (debug)", + "type": "debugpy", "request": "launch", "module": "pytest", "args": [ @@ -19,14 +19,14 @@ }, { "name": "Python: file as Module", - "type": "python", + "type": "debugpy", "request": "launch", "module": "stubber.${fileBasenameNoExtension}", "justMyCode": false }, { "name": "mpremote", - "type": "python", + "type": "debugpy", "request": "launch", "module": "mpremote", "justMyCode": false, @@ -39,18 +39,18 @@ }, { "name": "Python: stubber - cmdline", - "type": "python", + "type": "debugpy", "request": "launch", "module": "stubber.stubber", "cwd": "${workspaceFolder}", "args": [ - "-v", - "make-variants", + // "-v", + "get-frozen", + "--version", + "preview", // "switch", // "v1.20.0", // "build", - // "--version", - // "latest", // "--port", // "esp32", // "--board", @@ -85,25 +85,29 @@ ] }, { - "name": "Python: stubber get-docstubs", - "type": "python", + "name": "Python: board_stubber", + "type": "debugpy", "request": "launch", - "module": "stubber.stubber", + "program": "scripts/board_stubber.py", + "cwd": "${workspaceFolder}", "args": [ - "get-docstubs" - ] + "--format", + "mpy", + "--debug" + ], + "console": "integratedTerminal" }, { "name": "Python: Current File", - "type": "python", + "type": "debugpy", "request": "launch", "program": "${file}", - // "cwd": "${fileDirname}", + "cwd": "${workspaceFolder}", "console": "integratedTerminal" }, { "name": "Python: this File - Args ", - "type": "python", + "type": "debugpy", "request": "launch", "program": "${file}", "console": "integratedTerminal", @@ -113,7 +117,7 @@ }, { "name": "Micro-CPython: Current File", - "type": "python", + "type": "debugpy", "subProcess": false, "justMyCode": true, "request": "launch", @@ -129,7 +133,7 @@ }, { "name": "Python: Debug process --path", - "type": "python", + "type": "debugpy", "request": "launch", "console": "integratedTerminal", "program": "${file}", @@ -143,7 +147,7 @@ "purpose": [ "debug-test" ], - "type": "python", + "type": "debugpy", "request": "launch", "console": "integratedTerminal", "justMyCode": false, diff --git a/.vscode/settings.json b/.vscode/settings.json index 28608a455..4941d8665 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -140,5 +140,8 @@ ], "python.analysis.enablePytestExtra": true, "testExplorer.addToEditorContextMenu": true, - "todo-tree.tree.scanMode": "open files" + "todo-tree.tree.scanMode": "open files", + "python.analysis.typeshedPaths": [ + "typings" + ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c04afd0b3..2bc92caa5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,36 +8,22 @@ "detail": "run createstubs in linux port", "type": "shell", "windows": { - "command": "ubuntu run MICROPYPATH=./board ./tools/micropython board/createstubs.py" + "command": "ubuntu run MICROPYPATH=./src/stubber/board ./tests/tools/ubuntu_20_04/micropython_v1_21_0 src/stubber/board/createstubs.py" }, "linux": { - "command": "MICROPYPATH=./board ./tools/micropython board/createstubs.py" + "command": "MICROPYPATH=./src/stubber/board ./tests/tools/ubuntu_20_04/micropython_v1_21_0 src/stubber/board/createstubs.py" }, "problemMatcher": [] }, { - "label": "minify", - "detail": "minify for esp8622", - "type": "shell", - "command": "poetry run stubber minify --all --compile", - "problemMatcher": [], - "group": { - "kind": "build", - "isDefault": true - } - }, - { - "label": "run minified", - "detail": "run minified createstubs in linux port", - "dependsOn": [ - "minify" - ], + "label": "run createstubs_db", + "detail": "run createstubs_db in linux port", "type": "shell", "windows": { - "command": "ubuntu run ./tools/micropython minified/createstubs.py" + "command": "ubuntu run MICROPYPATH=./src/stubber/board ./tests/tools/ubuntu_20_04/micropython_v1_21_0 src/stubber/board/createstubs_db.py" }, "linux": { - "command": "./tools/micropython minified/createstubs.py" + "command": "MICROPYPATH=./src/stubber/board ./tests/tools/ubuntu_20_04/micropython_v1_21_0 src/stubber/board/createstubs_db.py" }, "problemMatcher": [] }, diff --git a/data/micropython_v1_22_0_rpi_pico_w.txt b/data/micropython_v1_22_0_rpi_pico_w.txt new file mode 100644 index 000000000..0ba8abd4d --- /dev/null +++ b/data/micropython_v1_22_0_rpi_pico_w.txt @@ -0,0 +1,18 @@ +# MicroPython v1.22.0 on 2023-12-27; Raspberry Pi Pico W with RP2040 +__main__ array framebuf random +_asyncio asyncio/__init__ gc re +_boot asyncio/core hashlib requests/__init__ +_boot_fat asyncio/event heapq rp2 +_onewire asyncio/funcs io select +_rp2 asyncio/lock json socket +_thread asyncio/stream lwip ssl +_webrepl binascii machine struct +aioble/__init__ bluetooth math sys +aioble/central builtins micropython time +aioble/client cmath mip/__init__ uasyncio +aioble/core collections neopixel uctypes +aioble/device cryptolib network urequests +aioble/l2cap deflate ntptime webrepl +aioble/peripheral dht onewire webrepl_setup +aioble/security ds18x20 os websocket +aioble/server errno platform \ No newline at end of file diff --git a/data/readme.md b/data/readme.md new file mode 100644 index 000000000..612cce1d0 --- /dev/null +++ b/data/readme.md @@ -0,0 +1,14 @@ +# Overview of modules avaialble on different ports and boards +These files are used to compile a list of modules that createstubs.py and its variants will attempt to create stubs for. + +This can be generated by running the following command in micropython on the board: `help('modules')` +or via mpremote by running the following command: `mpremote exec "help('modules')"` + +Store the ouput in a file called `micropython--[-].txt` in this directory. +place a comment `#` before all lines that do not not contain module names. + +After adding or changing any file update the list of modules using from the root of the repository. +``` +stubber update-module-list +stubber make-variants +``` diff --git a/mip/full.json b/mip/full.json index 315071fc0..b7501ed23 100644 --- a/mip/full.json +++ b/mip/full.json @@ -1,30 +1,30 @@ { - "urls": [ - [ - "createstubs.py", - "github:josverl/micropython-stubber/src/stubber/board/createstubs.py" - ], - [ - "createstubs_db.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db.py" - ], - [ - "createstubs_mem.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem.py" - ], - [ - "logging.py", - "github:Josverl/micropython-stubber/src/stubber/board/logging.py" - ], - [ - "modules.txt", - "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" - ], - [ - "board_info.csv", - "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" - ] + "urls": [ + [ + "createstubs.py", + "github:josverl/micropython-stubber/src/stubber/board/createstubs.py" ], - "deps": [], - "version": "1.16.2" -} \ No newline at end of file + [ + "createstubs_db.py", + "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db.py" + ], + [ + "createstubs_mem.py", + "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem.py" + ], + [ + "logging.py", + "github:Josverl/micropython-stubber/src/stubber/board/logging.py" + ], + [ + "modulelist.txt", + "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" + ], + [ + "board_info.csv", + "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" + ] + ], + "deps": [], + "version": "1.17.0" +} diff --git a/mip/minified.json b/mip/minified.json index b2532f514..2adbb29f0 100644 --- a/mip/minified.json +++ b/mip/minified.json @@ -1,26 +1,26 @@ { - "urls": [ - [ - "createstubs.py", - "github:josverl/micropython-stubber/src/stubber/board/createstubs_min.py" - ], - [ - "createstubs_db.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db_min.py" - ], - [ - "createstubs_mem.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem_min.py" - ], - [ - "modules.txt", - "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" - ], - [ - "board_info.csv", - "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" - ] + "urls": [ + [ + "createstubs.py", + "github:josverl/micropython-stubber/src/stubber/board/createstubs_min.py" ], - "deps": [], - "version": "1.16.2" -} \ No newline at end of file + [ + "createstubs_db.py", + "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db_min.py" + ], + [ + "createstubs_mem.py", + "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem_min.py" + ], + [ + "modulelist.txt", + "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" + ], + [ + "board_info.csv", + "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" + ] + ], + "deps": [], + "version": "1.17.0" +} diff --git a/mip/mpy_v5.json b/mip/mpy_v5.json index b6722c750..a38d05284 100644 --- a/mip/mpy_v5.json +++ b/mip/mpy_v5.json @@ -1,26 +1,26 @@ { - "urls": [ - [ - "createstubs.mpy", - "github:josverl/micropython-stubber/mip/v5/createstubs_mpy.mpy" - ], - [ - "createstubs_db.mpy", - "github:Josverl/micropython-stubber/mip/v5/createstubs_db_mpy.mpy" - ], - [ - "createstubs_mem.mpy", - "github:Josverl/micropython-stubber/mip/v5/createstubs_mem_mpy.mpy" - ], - [ - "modulelist.txt", - "github:Josverl/micropython-stubber/mip/v5/modulelist.txt" - ], - [ - "board_info.csv", - "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" - ] + "urls": [ + [ + "createstubs.mpy", + "github:josverl/micropython-stubber/mip/v5/createstubs_mpy.mpy" ], - "deps": [], - "version": "1.16.2" -} \ No newline at end of file + [ + "createstubs_db.mpy", + "github:Josverl/micropython-stubber/mip/v5/createstubs_db_mpy.mpy" + ], + [ + "createstubs_mem.mpy", + "github:Josverl/micropython-stubber/mip/v5/createstubs_mem_mpy.mpy" + ], + [ + "modulelist.txt", + "github:Josverl/micropython-stubber/mip/v5/modulelist.txt" + ], + [ + "board_info.csv", + "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" + ] + ], + "deps": [], + "version": "1.17.0" +} diff --git a/mip/mpy_v6.json b/mip/mpy_v6.json index 85311c517..f174662c8 100644 --- a/mip/mpy_v6.json +++ b/mip/mpy_v6.json @@ -1,26 +1,26 @@ { - "urls": [ - [ - "createstubs.mpy", - "github:josverl/micropython-stubber/mip/v6/createstubs_mpy.mpy" - ], - [ - "createstubs_mem.mpy", - "github:Josverl/micropython-stubber/mip/v6/createstubs_mem_mpy.mpy" - ], - [ - "createstubs_db.mpy", - "github:Josverl/micropython-stubber/mip/v6/createstubs_db_mpy.mpy" - ], - [ - "modulelist.txt", - "github:Josverl/micropython-stubber/mip/v6/modulelist.txt" - ], - [ - "board_info.csv", - "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" - ] + "urls": [ + [ + "createstubs.mpy", + "github:josverl/micropython-stubber/mip/v6/createstubs_mpy.mpy" ], - "deps": [], - "version": "1.16.2" -} \ No newline at end of file + [ + "createstubs_mem.mpy", + "github:Josverl/micropython-stubber/mip/v6/createstubs_mem_mpy.mpy" + ], + [ + "createstubs_db.mpy", + "github:Josverl/micropython-stubber/mip/v6/createstubs_db_mpy.mpy" + ], + [ + "modulelist.txt", + "github:Josverl/micropython-stubber/mip/v6/modulelist.txt" + ], + [ + "board_info.csv", + "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" + ] + ], + "deps": [], + "version": "1.17.0" +} diff --git a/mip/package.json b/mip/package.json deleted file mode 100644 index f52c054d0..000000000 --- a/mip/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "urls": [ - [ - "createstubs.py", - "github:josverl/micropython-stubber/src/stubber/board/createstubs.py" - ], - [ - "createstubs_db.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db.py" - ], - [ - "createstubs_mem.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem.py" - ], - [ - "logging.py", - "github:Josverl/micropython-stubber/src/stubber/board/logging.py" - ], - [ - "modules.txt", - "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" - ], - [ - "board_info.csv", - "github:Josverl/micropython-stubber/src/stubber/board/board_info.csv" - ] - ], - "deps": [], - "version": "1.11.2" -} \ No newline at end of file diff --git a/mip/v5/createstubs.py b/mip/v5/createstubs.py index 8e4714a8c..2ab22f8ed 100644 --- a/mip/v5/createstubs.py +++ b/mip/v5/createstubs.py @@ -4,11 +4,14 @@ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -20,11 +23,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.0" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -32,24 +73,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -59,11 +97,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -82,20 +120,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -110,11 +151,7 @@ def get_obj_attributes(self, item_instance: object): order = 4 _result.append((name, repr(val), repr(type(val)), val, order)) except AttributeError as e: - _errors.append( - "Couldn't get attribute '{}' from object '{}', Err: {}".format( - name, item_instance, e - ) - ) + _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e)) except MemoryError as e: print("MemoryError: {}".format(e)) sleep(1) @@ -132,21 +169,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -161,10 +200,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -178,12 +217,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info( - "Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1) - ) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -195,40 +232,35 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: module_name, self._fwid, info_, __version__ ) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write( + "from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n" + ) self.write_object_stub(fp, new_module, module_name, "") - self._report.append( - '{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/")) - ) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True - def write_object_stub( - self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0 - ): + def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0): "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -236,11 +268,16 @@ def write_object_stub( # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -259,11 +296,11 @@ def write_object_stub( if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -277,11 +314,7 @@ def write_object_stub( s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug( - "# def {1} function/method/closure, type = '{0}'".format( - item_type_txt, item_name - ) - ) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -292,16 +325,14 @@ def write_object_stub( first = "self, " # class method - add function decoration if "bound_method" in item_type_txt or "bound_method" in item_repr: - s = "{}@classmethod\n".format( - indent - ) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret) - else: - s = "{}def {}({}*args, **kwargs) -> {}:\n".format( - indent, item_name, first, ret + s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format( + indent, item_name, ret ) + else: + s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -311,38 +342,42 @@ def write_object_stub( t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format( - indent, item_name, t, item_type_txt, item_repr - ) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -358,7 +393,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -376,45 +411,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info( - "Created stubs for {} modules on board {}\nPath: {}".format( - len(self._report), self._fwid, self.path - ) - ) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -440,85 +483,85 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", - "port": "stm32" - if sys.platform.startswith("pyb") - else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = ( - sys.implementation._machine - if "_machine" in dir(sys.implementation) - else os.uname().machine + _machine = ( + sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore ) - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -540,14 +583,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") - and info["version"] - >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -571,25 +614,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -642,10 +691,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -715,6 +760,7 @@ def main(): "ak8963", "apa102", "apa106", + "argparse", "array", "asyncio/__init__", "asyncio/core", @@ -763,6 +809,7 @@ def main(): "esp32", "espidf", "espnow", + "ffi", "flashbdev", "framebuf", "freesans20", @@ -799,6 +846,7 @@ def main(): "micropython", "mip", "mip/__init__", + "mip/__main__", "motor", "mpu6500", "mpu9250", @@ -826,6 +874,7 @@ def main(): "queue", "random", "requests", + "requests/__init__", "rp2", "rtch", "samd", @@ -838,6 +887,7 @@ def main(): "stm", "struct", "sys", + "termios", "time", "tpcalib", "uarray", @@ -905,16 +955,9 @@ def main(): gc.collect() stubber.create_all_stubs() - stubber.report() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/mip/v5/createstubs_db.py b/mip/v5/createstubs_db.py index 0c7225a05..38f14d899 100644 --- a/mip/v5/createstubs_db.py +++ b/mip/v5/createstubs_db.py @@ -18,16 +18,19 @@ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device -This variant was generated from createstubs.py by micropython-stubber v1.16.0 +This variant was generated from createstubs.py by micropython-stubber v1.17.0 """ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -39,11 +42,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.0" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -51,24 +92,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -78,11 +116,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -101,20 +139,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -147,21 +188,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -176,10 +219,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -193,10 +236,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -206,21 +249,18 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") - self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True @@ -228,14 +268,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -243,11 +283,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -266,11 +311,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -284,7 +329,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -300,7 +345,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -310,36 +355,42 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -355,7 +406,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -373,41 +424,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -433,79 +496,83 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", - "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -527,13 +594,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -557,25 +625,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -628,10 +702,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -645,92 +715,99 @@ def is_micropython() -> bool: return True -def main(): - import machine # type: ignore +SKIP_FILE = "modulelist.done" + +def get_modules(skip=0): + # new + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + try: + with open(fname) as f: + i = 0 + while True: + line = f.readline().strip() + if not line: + break + if len(line) > 0 and line[0] == "#": + continue + i += 1 + if i < skip: + continue + yield line + break + except OSError: + pass + + +def write_skip(done): + # write count of modules already processed to file + with open(SKIP_FILE, "w") as f: + f.write(str(done) + "\n") + + +def read_skip(): + # read count of modules already processed from file + done = 0 try: - f = open("modulelist.done", "r+b") - was_running = True - print("Opened existing db") + with open(SKIP_FILE) as f: + done = int(f.readline().strip()) except OSError: - f = open("modulelist.done", "w+b") - print("created new db") - was_running = False + pass + return done + + +def main(): + import machine # type: ignore + + was_running = file_exists(SKIP_FILE) + if was_running: + log.info("Continue from last run") + else: + log.info("Starting new run") + # try: + # f = open("modulelist.done", "r+b") + # was_running = True + # print("Continue from last run") + # except OSError: + # f = open("modulelist.done", "w+b") + # was_running = False stubber = Stubber(path=read_path()) # f_name = "{}/{}".format(stubber.path, "modules.json") + skip = 0 if not was_running: # Only clean folder if this is a first run stubber.clean() - # get list of modules to process - get_modulelist(stubber) - # remove the ones that are already done - modules_done = {} # type: dict[str, str] - try: - with open("modulelist.done") as f: - # not optimal , but works on mpremote and esp8266 - for line in f.read().split("\n"): - line = line.strip() - gc.collect() - if len(line) > 0: - key, value = line.split("=", 1) - modules_done[key] = value - except (OSError, SyntaxError): - pass - gc.collect() - # see if we can continue from where we left off - modules = [m for m in stubber.modules if m not in modules_done.keys()] - gc.collect() - for modulename in modules: + stubber.report_start("modules.json") + else: + skip = read_skip() + stubber._json_name = "{}/{}".format(stubber.path, "modules.json") + + for modulename in get_modules(skip): # ------------------------------------ # do epic shit # but sometimes things fail / run out of memory and reboot - ok = False try: - ok = stubber.create_one_stub(modulename) + stubber.create_one_stub(modulename) except MemoryError: # RESET AND HOPE THAT IN THE NEXT CYCLE WE PROGRESS FURTHER machine.reset() # ------------------------------------- gc.collect() - modules_done[modulename] = str(stubber._report[-1] if ok else "failed") - with open("modulelist.done", "a") as f: - f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + # modules_done[modulename] = str(stubber._report[-1] if ok else "failed") + # with open("modulelist.done", "a") as f: + # f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + skip += 1 + write_skip(skip) - # Finished processing - load all the results , and remove the failed ones - if modules_done: - # stubber.write_json_end(mod_fp) - stubber._report = [v for _, v in modules_done.items() if v != "failed"] - stubber.report() - - -def get_modulelist(stubber): - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - with open(p + "/modulelist.txt") as f: - print("DEBUG: list of modules: " + p + "/modulelist.txt") - for line in f.read().split("\n"): - line = line.strip() - if len(line) > 0 and line[0] != "#": - stubber.modules.append(line) - gc.collect() - break - except OSError: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - _log.warn("Could not find modulelist.txt, using default modules") - gc.collect() + print("All modules have been processed, Finalizing report") + stubber.report_end() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/mip/v5/createstubs_db_min.py b/mip/v5/createstubs_db_min.py index 3e0a7370d..f3bc2d297 100644 --- a/mip/v5/createstubs_db_min.py +++ b/mip/v5/createstubs_db_min.py @@ -1,301 +1,325 @@ -v='stubber' -u='{}/{}' -t='method' -s='function' -r='bool' -q='str' -p='float' -o='int' -n=NameError -m=sorted -l=MemoryError -k=NotImplementedError -b=',\n' -a='dict' -Z='list' -Y='tuple' -X='micropython' -W=str -V=repr -T='_' -S=KeyError -R=IndexError -Q=dir -P=ImportError -O='family' -N=print -M=True -L=len -K='board' +A2='No report file' +A1='Failed to create the report.' +A0='method' +z='function' +y='bool' +x='str' +w='float' +v='int' +u='micropython' +t='stubber' +s=TypeError +r=Exception +q=KeyError +p=sorted +o=MemoryError +n=NotImplementedError +j=',\n' +i='modules.json' +h='{}/{}' +g='w' +f='dict' +e='list' +d='tuple' +c=str +b=repr +W='-preview' +V='-' +U='board' +T=IndexError +S=print +R=True +Q='family' +P=len +O=ImportError +N=dir +M=open +K='port' J='.' -I=open -H=AttributeError +I=AttributeError +H=False G='/' -F=False E=None -D='version' -A=OSError -C='' -import gc as B,os,sys -from ujson import dumps as c -try:from machine import reset -except P:pass -try:from collections import OrderedDict as d -except P:from ucollections import OrderedDict as d -__version__='v1.16.0' -w=2 -x=2 -e=[J,'/lib','/sd/lib','/flash/lib','lib'] +D=OSError +C='version' +B='' +import gc as F,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except O:pass +try:from collections import OrderedDict as k +except O:from ucollections import OrderedDict as k +__version__='v1.17.0' +A3=2 +A4=2 +A5=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(t) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(C,path=E,firmware_id=E): - D=firmware_id + def __init__(B,path=E,firmware_id=E): + C=firmware_id try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise k('MicroPython 1.13.0 cannot be stubbed') - except H:pass - C._report=[];C.info=_info();B.collect() - if D:C._fwid=D.lower() - elif C.info[O]==X:C._fwid='{family}-{ver}-{port}-{board}'.format(**C.info) - else:C._fwid='{family}-{ver}-{port}'.format(**C.info) - C._start_free=B.mem_free() + if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise n('MicroPython 1.13.0 cannot be stubbed') + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));F.collect() + if C:B._fwid=C.lower() + elif B.info[Q]==u:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=F.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - C.path='{}/stubs/{}'.format(path,C.flat_fwid).replace('//',G) - try:f(path+G) - except A:N('error creating stub folder {}'.format(path)) - C.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];C.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];C.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:X(path+G) + except D:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in Q(I): - if A.startswith(T)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in N(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=V(type(E)).split("'")[1] - except R:F=C - if F in{o,p,q,r,Y,Z,a}:G=1 - elif F in{s,t}:G=2 - elif F in'class':G=3 + D=getattr(H,A) + try:E=b(type(D)).split("'")[1] + except T:E=B + if E in{v,w,x,y,d,e,f}:G=1 + elif E in{z,A0}:G=2 + elif E in'class':G=3 else:G=4 - D.append((A,V(E),V(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except l as K:sleep(1);reset() - D=m([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);B.collect();return D,J - def add_modules(A,modules):A.modules=m(set(A.modules)|set(modules)) - def create_all_stubs(A): - B.collect() - for C in A.modules:A.create_one_stub(C) + C.append((A,b(D),b(type(D)),D,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except o as J:S('MemoryError: {}'.format(J));sleep(1);reset() + C=p([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K + def add_modules(A,modules):A.modules=p(set(A.modules)|set(modules)) + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') def create_one_stub(C,module_name): - D=module_name - if D in C.problematic:return F - if D in C.excluded:return F - H='{}/{}.py'.format(C.path,D.replace(J,G));B.collect();E=F - try:E=C.create_module_stub(D,H) - except A:return F - B.collect();return E + B=module_name + if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H + if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H + I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();E=H + try:E=C.create_module_stub(B,I) + except D:return H + F.collect();return E def create_module_stub(K,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(J,T)+'.py';H=K.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,J) - L=E - try:L=__import__(D,E,E,'*');U=B.mem_free() - except P:return F - f(H) - with I(H,'w')as N:Q=W(K.info).replace('OrderedDict(',C).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,K._fwid,Q,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');K.write_object_stub(N,L,D,C) - K._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del L - except(A,S):pass - try:del sys.modules[D] - except S:pass - B.collect();return M + I=file_name;C=module_name + if I is E:L=C.replace(J,'_')+'.pyi';I=K.path+G+L + else:L=I.split(G)[-1] + if G in C:C=C.replace(G,J) + N=E + try:N=__import__(C,E,E,'*');Q=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,Q)) + except O:return H + X(I) + with M(I,g)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) + K.report_add(C,I) + if C not in{'os','sys','logging','gc'}: + try:del N + except(D,q):A.warning('could not del new_module') + F.collect();return R def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';Q=in_class;P=object_expr;O='Exception';H=fp;E=indent;B.collect() - if P in K.problematic:return - R,M=K.get_obj_attributes(P) - if M:N(M) - for(F,J,G,T,f)in R: - if F in['classmethod','staticmethod','BaseException',O]:continue - if F[0].isdigit():continue - if G==""and L(E)<=x*4: - U=C;V=F.endswith(O)or F.endswith('Error')or F in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=O - D='\n{}class {}({}):\n'.format(E,F,U) - if V:D+=E+' ...\n';H.write(D);return - H.write(D);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,F),E+' ',Q+1);D=E+' def __init__(self, *argv, **kwargs) -> None:\n';D+=E+' ...\n\n';H.write(D) - elif any(A in G for A in[t,s,'closure']): - W=b;X=C - if Q>0:X='self, ' - if c in G or c in J:D='{}@classmethod\n'.format(E)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(E,F,W) - else:D='{}def {}({}*args, **kwargs) -> {}:\n'.format(E,F,X,W) - D+=E+' ...\n\n';H.write(D) - elif G=="":0 - elif G.startswith(""and P(D)<=A4*4: + Q=B;R=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] + if R:Q=M + C='\n{}class {}({}):\n'.format(D,E,Q) + if R:C+=D+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C) + elif any(A in H for A in[A0,z,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S) + C+=D+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=C - if A[D]==C and sys.platform not in('unix','win32'): - try:l=os.uname();A[D]=l.release - except(R,H,TypeError):pass - for(m,n,o)in[(i,i,'const'),(j,j,'FAT'),(k,'pybricks.hubs','EV3Brick')]: - try:p=__import__(n,E,E,o);A[O]=m;del p;break - except(P,S):pass - if A[O]==k:A['release']='2.0.0' - if A[O]==X: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - N=int(A[G]);Z=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][N>>10] - if Z:A[c]=Z - A[G]='v{}.{}'.format(N&255,N>>8&3) - A[a]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def g(info,board_descr,filename): - with I(filename,'r')as B: - while 1: - A=B.readline() - if not A:break - C,D=A.split(',')[0].strip(),A.split(',')[1].strip() - if C==board_descr:info[K]=D;return M - return F -def get_root(): - try:B=os.getcwd() - except(A,H):B=J - C=B - for C in[B,'/sd','/flash',G,J]: - try:D=os.stat(C);break - except A:continue + if'uname'in N(os): + A[D]=Y(os.uname()[3]) + if not A[D]:A[D]=Y(os.uname()[2]) + elif C in N(sys):A[D]=Y(sys.version) + except(I,T,s):pass + if A[C]==B and sys.platform not in(S,R): + try:a=os.uname();A[C]=a.release + except(T,I,s):pass + for(b,c,d)in[(V,V,'const'),(X,X,'FAT'),(Z,'pybricks.hubs','EV3Brick')]: + try:e=__import__(c,E,E,d);A[Q]=b;del e;break + except(O,q):pass + if A[Q]==Z:A['release']='2.0.0' + if A[Q]==u: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[P]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[D]and not A[C].endswith(W):A[C]=A[C]+W + A[L]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A +def A6(version): + A=version;B=J.join([c(A)for A in A[:3]]) + if P(A)>3 and A[3]:B+=V+A[3] + return B +def A7(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except O:A.warning('BOARDNAME not found');C=B return C -def h(filename): +def get_root(): + try:A=os.getcwd() + except(D,I):A=J + B=A + for B in[A,'/sd','/flash',G,J]: + try:C=os.stat(B);break + except D:continue + return B +def Z(filename): try: - if os.stat(filename)[0]>>14:return M - return F - except A:return F -def i():sys.exit(1) + if os.stat(filename)[0]>>14:return R + return H + except D:return H +def l():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): - path=C - if L(sys.argv)==3: + path=B + if P(sys.argv)==3: A=sys.argv[1].lower() if A in('--path','-p'):path=sys.argv[2] - else:i() - elif L(sys.argv)==2:i() + else:l() + elif P(sys.argv)==2:l() return path -def j(): - try:A=bytes('abc',encoding='utf8');B=j.__module__;return F - except(k,H):return M -def main(): - K='failed';G='modulelist.done';import machine as O - try:C=I(G,'r+b');N=M - except A:C=I(G,'w+b');N=F - stubber=Stubber(path=read_path()) - if not N:stubber.clean() - y(stubber);D={} - try: - with I(G)as C: - for E in C.read().split('\n'): - E=E.strip();B.collect() - if L(E)>0:P,Q=E.split('=',1);D[P]=Q - except(A,SyntaxError):pass - B.collect();R=[A for A in stubber.modules if A not in D.keys()];B.collect() - for H in R: - J=F - try:J=stubber.create_one_stub(H) - except l:O.reset() - B.collect();D[H]=W(stubber._report[-1]if J else K) - with I(G,'a')as C:C.write('{}={}\n'.format(H,'ok'if J else K)) - if D:stubber._report=[A for(B,A)in D.items()if A!=K];stubber.report() -def y(stubber): - E='/modulelist.txt';stubber.modules=[] - for D in e: +def m(): + try:A=bytes('abc',encoding='utf8');B=m.__module__;return H + except(n,I):return R +a='modulelist.done' +def A8(skip=0): + for E in A5: + B=E+'/modulelist.txt' + if not Z(B):continue try: - with I(D+E)as F: - N('DEBUG: list of modules: '+D+E) - for C in F.read().split('\n'): - C=C.strip() - if L(C)>0 and C[0]!='#':stubber.modules.append(C) - B.collect();break - except A:pass - if not stubber.modules:stubber.modules=[X];_log.warn('Could not find modulelist.txt, using default modules') - B.collect() -if __name__=='__main__'or j(): - try:z=logging.getLogger(v);logging.basicConfig(level=logging.INFO) - except n:pass - if not h('no_auto_stubber.txt'): - try:B.threshold(4*1024);B.enable() + with M(B)as F: + C=0 + while R: + A=F.readline().strip() + if not A:break + if P(A)>0 and A[0]=='#':continue + C+=1 + if C bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) @@ -202,10 +223,11 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + # lets not try - most times it does not work anyway + # try: + # del sys.modules[module_name] + # except KeyError: + # self.log.warning("could not del sys.modules[{}]".format(module_name)) gc.collect() return True @@ -231,8 +253,13 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, self.log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + self.log.info("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -251,7 +278,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods @@ -304,12 +331,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ["object", "set", "frozenset", "Pin", "FileIO"]: # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + s = "{0}{1} : {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) self.log.debug("\n" + s) else: @@ -319,12 +348,12 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -338,6 +367,7 @@ def flat_fwid(self): def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" + wdt.feed() if path is None: path = self.path self.log.info("Clean/remove files in folder: {}".format(path)) @@ -360,6 +390,7 @@ def clean(self, path: str = None): # type: ignore def report(self, filename: str = "modules.json"): "create json with list of exported modules" + wdt.feed() self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) f_name = "{}/{}".format(self.path, filename) self.log.info("Report file: {}".format(f_name)) @@ -418,12 +449,21 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] @@ -433,21 +473,29 @@ def _info(): # type:() -> dict[str, str] "version": "", "build": "", "ver": "", - "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( sys.implementation._mpy if "_mpy" in dir(sys.implementation) @@ -458,39 +506,28 @@ def _info(): # type:() -> dict[str, str] except (AttributeError, IndexError): pass gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") + read_boardname(info) gc.collect() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -512,13 +549,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -542,25 +580,94 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def read_boardname(info, desc: str = ""): + info["board"] = info["board"].replace(" ", "_") + found = False + for filename in [d + "/board_name.txt" for d in LIBS]: + wdt.feed() + # print("look up the board name in the file", filename) + if file_exists(filename): + with open(filename, "r") as file: + data = file.read() + if data: + info["board"] = data.strip() + found = True break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False + if not found: + print("Board not found, guessing board name") + descr = "" + # descr = desc or info["board"].strip() + # if "with " + info["cpu"].upper() in descr: + # # remove the with cpu part + # descr = descr.split("with " + info["cpu"].upper())[0].strip() + info["board"] = descr + + +# def read_boardname(info, desc: str = ""): +# wdt.feed() +# # print("look up the board name in the file", filename) +# if file_exists(filename): +# descr = desc or info["board"].strip() +# pos = descr.rfind(" with") +# if pos != -1: +# short_descr = descr[:pos].strip() +# else: +# short_descr = "" +# print("searching info file: {} for: '{}' or '{}'".format(filename, descr, short_descr)) +# if find_board(info, descr, filename, short_descr): +# found = True +# break +# if not found: +# print("Board not found, guessing board name") +# descr = desc or info["board"].strip() +# if "with " + info["cpu"].upper() in descr: +# # remove the with cpu part +# descr = descr.split("with " + info["cpu"].upper())[0].strip() +# info["board"] = descr +# info["board"] = info["board"].replace(" ", "_") +# gc.collect() + + +# def find_board(info: dict, descr: str, filename: str, short_descr: str): +# "Find the board in the provided board_info.csv file" +# short_hit = "" +# with open(filename, "r") as file: +# # ugly code to make testable in python and micropython +# # TODO: This is VERY slow on micropython whith MPREMOTE mount on esp32 (2-3 minutes to read file) +# while 1: +# line = file.readline() +# if not line: +# break +# descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() +# if descr_ == descr: +# info["board"] = board_ +# return True +# elif short_descr and descr_ == short_descr: +# if "with" in short_descr: +# # Good enough - no need to trawl the entire file +# info["board"] = board_ +# return True +# # good enough if not found in the rest of the file (but slow) +# short_hit = board_ +# if short_hit: +# info["board"] = short_hit +# return True +# return False def get_root() -> str: # sourcery skip: use-assigned-variable diff --git a/mip/v5/createstubs_lvgl_min.py b/mip/v5/createstubs_lvgl_min.py index eec6d93d1..cdab9a5ad 100644 --- a/mip/v5/createstubs_lvgl_min.py +++ b/mip/v5/createstubs_lvgl_min.py @@ -1,275 +1,783 @@ -t='stubber' -s='{}/{}' -r='method' -q='function' -p='bool' -o='str' -n='float' -m='int' -l='micropython' -k=Exception -j=NameError -i=sorted -h=NotImplementedError -Z=',\n' -Y='dict' -X='list' -W='tuple' -V=open -U=repr -S='_' -R=len -Q=KeyError -P=IndexError -O=dir -N=print -M=ImportError -L=True -K='family' -J='board' -I='.' -H=AttributeError -A=False -G='/' -E=None -D='version' -F=OSError -B='' -import gc as C,os,sys -from ujson import dumps as a -try:from machine import reset -except M:pass -try:from collections import OrderedDict as b -except M:from ucollections import OrderedDict as b -__version__='v1.16.0' -u=2 -v=2 -w=[I,'/lib','/sd/lib','/flash/lib','lib'] +""" +Create stubs for the lvgl modules on a MicroPython board. + +Note that the stubs can be very large, and it may be best to directly store them on an SD card if your device supports this. + +This variant was generated from createstubs.py by micropython-stubber v1.16.2 +""" +# Copyright (c) 2019-2023 Jos Verlinde + +import gc +# import logging +import os +import sys from time import sleep + +try: + from ujson import dumps +except: + from json import dumps + +try: + from machine import reset # type: ignore +except ImportError: + pass + +try: + from collections import OrderedDict +except ImportError: + from ucollections import OrderedDict # type: ignore + +try: + from nope_machine import WDT + + wdt = WDT() + +except ImportError: + + class _WDT: + def feed(self): + pass + + wdt = _WDT() + + +wdt.feed() + +__version__ = "v1.16.2" +ENOENT = 2 +_MAX_CLASS_LEVEL = 2 # Max class nesting +LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] + + class Stubber: - def __init__(A,path=E,firmware_id=E): - B=firmware_id - try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise h('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();C.collect() - if B:A._fwid=B.lower() - elif A.info[K]==l:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=C.mem_free() - if path: - if path.endswith(G):path=path[:-1] - else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:c(path+G) - except F:N('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] - def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in O(I): - if A.startswith(S)and not A in L.modules:continue - try: - E=getattr(I,A) - try:F=U(type(E)).split("'")[1] - except P:F=B - if F in{m,n,o,p,W,X,Y}:G=1 - elif F in{q,r}:G=2 - elif F in'class':G=3 - else:G=4 - D.append((A,U(E),U(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=i([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);C.collect();return D,J - def add_modules(A,modules):A.modules=i(set(A.modules)|set(modules)) - def create_all_stubs(A): - C.collect() - for B in A.modules:A.create_one_stub(B) - def create_one_stub(B,module_name): - D=module_name - if D in B.problematic:return A - if D in B.excluded:return A - H='{}/{}.py'.format(B.path,D.replace(I,G));C.collect();E=A - try:E=B.create_module_stub(D,H) - except F:return A - C.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(I,S)+'.py';H=J.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');T=C.mem_free() - except M:return A - c(H) - with V(H,'w')as N:P=str(J.info).replace('OrderedDict(',B).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(N,K,D,B) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del K - except(F,Q):pass - try:del sys.modules[D] - except Q:pass - C.collect();return L - def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';P=in_class;O=object_expr;M='Exception';H=fp;D=indent;C.collect() - if O in K.problematic:return - S,L=K.get_obj_attributes(O) - if L:N(L) - for(E,J,G,T,f)in S: - if E in['classmethod','staticmethod','BaseException',M]:continue - if E[0].isdigit():continue - if G==""and R(D)<=v*4: - U=B;V=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=M - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',P+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[r,q,'closure']): - Z=b;a=B - if P>0:a='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,Z) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,a,Z) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith("5:A[F]=B - if A[D]==B and sys.platform not in('unix','win32'): - try:i=os.uname();A[D]=i.release - except(P,H,TypeError):pass - for(j,k,m)in[(f,f,'const'),(g,g,'FAT'),(h,'pybricks.hubs','EV3Brick')]: - try:n=__import__(k,E,E,m);A[K]=j;del n;break - except(M,Q):pass - if A[K]==h:A['release']='2.0.0' - if A[K]==l: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - U=int(A[G]);X=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][U>>10] - if X:A[a]=X - A[G]='v{}.{}'.format(U&255,U>>8&3) - A[Y]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def d(info,board_descr,filename): - with V(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return L - return A -def get_root(): - try:A=os.getcwd() - except(F,H):A=I - B=A - for B in[A,'/sd','/flash',G,I]: - try:C=os.stat(B);break - except F:continue - return B -def e(filename): - try: - if os.stat(filename)[0]>>14:return L - return A - except F:return A -def f():sys.exit(1) -def read_path(): - path=B - if R(sys.argv)==3: - A=sys.argv[1].lower() - if A in('--path','-p'):path=sys.argv[2] - else:f() - elif R(sys.argv)==2:f() - return path -def g(): - try:B=bytes('abc',encoding='utf8');C=g.__module__;return A - except(h,H):return L + "Generate stubs for modules in firmware" + + def __init__(self, path: str = None, firmware_id: str = None): # type: ignore + try: + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore + raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") + except AttributeError: + pass + # self.log = logging.getLogger("stubber") + self._report = [] # type: list[str] + self.info = _info() + # self.log.info("Port: {}".format(self.info["port"])) + # self.log.info("Board: {}".format(self.info["board"])) + gc.collect() + wdt.feed() + if firmware_id: + self._fwid = firmware_id.lower() + else: + if self.info["family"] == "micropython": + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") + else: + self._fwid = "{family}-v{version}-{port}".format(**self.info) + self._start_free = gc.mem_free() # type: ignore + + if path: + if path.endswith("/"): + path = path[:-1] + else: + path = get_root() + + self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") + # self.log.debug(self.path) + try: + ensure_folder(path + "/") + except OSError: + print("error creating stub folder {}".format(path)) + self.problematic = [ + "upip", + "upysh", + "webrepl_setup", + "http_client", + "http_client_ssl", + "http_server", + "http_server_ssl", + ] + self.excluded = [ + "webrepl", + "_webrepl", + "port_diag", + "example_sub_led.py", + "example_pub_button.py", + ] + # there is no option to discover modules from micropython, list is read from an external file. + self.modules = [] # type: list[str] + + def get_obj_attributes(self, item_instance: object): + "extract information of the objects members and attributes" + # name_, repr_(value), type as text, item_instance + _result = [] + _errors = [] + # self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + for name in dir(item_instance): + if name.startswith("_") and not name in self.modules: + continue + # self.log.debug("get attribute {}".format(name)) + try: + val = getattr(item_instance, name) + # name , item_repr(value) , type as text, item_instance, order + # self.log.debug("attribute {}:{}".format(name, val)) + try: + type_text = repr(type(val)).split("'")[1] + except IndexError: + type_text = "" + if type_text in {"int", "float", "str", "bool", "tuple", "list", "dict"}: + order = 1 + elif type_text in {"function", "method"}: + order = 2 + elif type_text in ("class"): + order = 3 + else: + order = 4 + _result.append((name, repr(val), repr(type(val)), val, order)) + except AttributeError as e: + _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e)) + except MemoryError as e: + # print("MemoryError: {}".format(e)) + sleep(1) + reset() + + # remove internal __ + # _result = sorted([i for i in _result if not (i[0].startswith("_"))], key=lambda x: x[4]) + _result = sorted([i for i in _result if not (i[0].startswith("__"))], key=lambda x: x[4]) + gc.collect() + return _result, _errors + + def add_modules(self, modules): + "Add additional modules to be exported" + self.modules = sorted(set(self.modules) | set(modules)) + + def create_all_stubs(self): + "Create stubs for all configured modules" + # self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + gc.collect() + for module_name in self.modules: + self.create_one_stub(module_name) + # self.log.info("Finally done") + + def create_one_stub(self, module_name: str): + wdt.feed() + if module_name in self.problematic: + # self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + return False + if module_name in self.excluded: + # self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + return False + + file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + gc.collect() + result = False + try: + result = self.create_module_stub(module_name, file_name) + except OSError: + return False + gc.collect() + return result + + def create_module_stub(self, module_name: str, file_name: str = None) -> bool: # type: ignore + """Create a Stub of a single python module + + Args: + - module_name (str): name of the module to document. This module will be imported. + - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + """ + if file_name is None: + fname = module_name.replace(".", "_") + ".py" + file_name = self.path + "/" + fname + else: + fname = file_name.split("/")[-1] + + if "/" in module_name: + # for nested modules + module_name = module_name.replace("/", ".") + + # import the module (as new_module) to examine it + new_module = None + try: + new_module = __import__(module_name, None, None, ("*")) + m1 = gc.mem_free() # type: ignore + # self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + + except ImportError: + # self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + return False + + # Start a new file + ensure_folder(file_name) + with open(file_name, "w") as fp: + # todo: improve header + info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") + s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) + fp.write(s) + fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n") + self.write_object_stub(fp, new_module, module_name, "") + + self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + + if module_name not in {"os", "sys", "logging", "gc"}: + # try to unload the module unless we use it + try: + del new_module + except (OSError, KeyError): # lgtm [py/unreachable-statement] + pass + # lets not try - most times it does not work anyway + # try: + # del sys.modules[module_name] + # except KeyError: + pass + gc.collect() + return True + + def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0): + "Write a module/object stub to an open file. Can be called recursive." + gc.collect() + if object_expr in self.problematic: + # self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + return + + # # self.log.debug("DUMP : {}".format(object_expr)) + items, errors = self.get_obj_attributes(object_expr) + + if errors: + print(errors) + + for item_name, item_repr, item_type_txt, item_instance, _ in items: + # name_, repr_(value), type as text, item_instance, order + if item_name in ["classmethod", "staticmethod", "BaseException", "Exception"]: + # do not create stubs for these primitives + continue + if item_name[0].isdigit(): + # self.log.warning("NameError: invalid name {}".format(item_name)) + continue + # Class expansion only on first 3 levels (bit of a hack) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # self.log.info("{0}class {1}:".format(indent, item_name)) + superclass = "" + is_exception = ( + item_name.endswith("Exception") + or item_name.endswith("Error") + or item_name + in [ + "KeyboardInterrupt", + "StopIteration", + "SystemExit", + ] + ) + if is_exception: + superclass = "Exception" + s = "\n{}class {}({}):\n".format(indent, item_name, superclass) + # s += indent + " ''\n" + if is_exception: + s += indent + " ...\n" + fp.write(s) + continue + # write classdef + fp.write(s) + # first write the class literals and methods + # self.log.debug("# recursion over class {0}".format(item_name)) + self.write_object_stub( + fp, + item_instance, + "{0}.{1}".format(obj_name, item_name), + indent + " ", + in_class + 1, + ) + # end with the __init__ method to make sure that the literals are defined + # Add __init__ + s = indent + " def __init__(self, *argv, **kwargs) -> None:\n" + s += indent + " ...\n\n" + fp.write(s) + elif any(word in item_type_txt for word in ["method", "function", "closure"]): + # self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # module Function or class method + # will accept any number of params + # return type Any/Incomplete + ret = "Incomplete" + first = "" + # Self parameter only on class methods/functions + if in_class > 0: + first = "self, " + # class method - add function decoration + if "bound_method" in item_type_txt or "bound_method" in item_repr: + s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret) + else: + s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) + s += indent + " ...\n\n" + fp.write(s) + # self.log.debug("\n" + s) + elif item_type_txt == "": + # Skip imported modules + # fp.write("# import {}\n".format(item_name)) + pass + + elif item_type_txt.startswith(" dict[str, str] + info = OrderedDict( + { + "family": sys.implementation.name, + "version": "", + "build": "", + "ver": "", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", + "cpu": "", + "mpy": "", + "arch": "", + } + ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" + try: + info["version"] = version_str(sys.implementation.version) # type: ignore + except AttributeError: + pass + try: + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() + info["mpy"] = ( + sys.implementation._mpy + if "_mpy" in dir(sys.implementation) + else sys.implementation.mpy + if "mpy" in dir(sys.implementation) + else "" + ) + except (AttributeError, IndexError): + pass + gc.collect() + read_boardname(info) + gc.collect() + + try: + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): + pass + # avoid build hashes + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" + + if info["version"] == "" and sys.platform not in ("unix", "win32"): + try: + u = os.uname() # type: ignore + info["version"] = u.release + except (IndexError, AttributeError, TypeError): + pass + # detect families + for fam_name, mod_name, mod_thing in [ + ("pycopy", "pycopy", "const"), + ("pycom", "pycom", "FAT"), + ("ev3-pybricks", "pybricks.hubs", "EV3Brick"), + ]: + try: + _t = __import__(mod_name, None, None, (mod_thing)) + info["family"] = fam_name + del _t + break + except (ImportError, KeyError): + pass + + if info["family"] == "ev3-pybricks": + info["release"] = "2.0.0" + + if info["family"] == "micropython": + info["version"] + if ( + info["version"] + and info["version"].endswith(".0") + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] <= "1.19.9" + ): + # versions from 1.10.0 to 1.20.0 do not have a micro .0 + info["version"] = info["version"][:-2] + + # spell-checker: disable + if "mpy" in info and info["mpy"]: # mpy on some v1.11+ builds + sys_mpy = int(info["mpy"]) + # .mpy architecture + arch = [ + None, + "x86", + "x64", + "armv6", + "armv6m", + "armv7m", + "armv7em", + "armv7emsp", + "armv7emdp", + "xtensa", + "xtensawin", + ][sys_mpy >> 10] + if arch: + info["arch"] = arch + # .mpy version.minor + info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" + # simple to use version[-build] string + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" + + return info + + +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def read_boardname(info, desc: str = ""): + info["board"] = info["board"].replace(" ", "_") + found = False + for filename in [d + "/board_name.txt" for d in LIBS]: + wdt.feed() + # # print("look up the board name in the file", filename) + if file_exists(filename): + with open(filename, "r") as file: + data = file.read() + if data: + info["board"] = data.strip() + found = True + break + if not found: + # print("Board not found, guessing board name") + descr = "" + # descr = desc or info["board"].strip() + # if "with " + info["cpu"].upper() in descr: + # # remove the with cpu part + # descr = descr.split("with " + info["cpu"].upper())[0].strip() + info["board"] = descr + + +# def read_boardname(info, desc: str = ""): +# wdt.feed() +# # # print("look up the board name in the file", filename) +# if file_exists(filename): +# descr = desc or info["board"].strip() +# pos = descr.rfind(" with") +# if pos != -1: +# short_descr = descr[:pos].strip() +# else: +# short_descr = "" +# # print("searching info file: {} for: '{}' or '{}'".format(filename, descr, short_descr)) +# if find_board(info, descr, filename, short_descr): +# found = True +# break +# if not found: +# # print("Board not found, guessing board name") +# descr = desc or info["board"].strip() +# if "with " + info["cpu"].upper() in descr: +# # remove the with cpu part +# descr = descr.split("with " + info["cpu"].upper())[0].strip() +# info["board"] = descr +# info["board"] = info["board"].replace(" ", "_") +# gc.collect() + + +# def find_board(info: dict, descr: str, filename: str, short_descr: str): +# "Find the board in the provided board_info.csv file" +# short_hit = "" +# with open(filename, "r") as file: +# # ugly code to make testable in python and micropython +# # TODO: This is VERY slow on micropython whith MPREMOTE mount on esp32 (2-3 minutes to read file) +# while 1: +# line = file.readline() +# if not line: +# break +# descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() +# if descr_ == descr: +# info["board"] = board_ +# return True +# elif short_descr and descr_ == short_descr: +# if "with" in short_descr: +# # Good enough - no need to trawl the entire file +# info["board"] = board_ +# return True +# # good enough if not found in the rest of the file (but slow) +# short_hit = board_ +# if short_hit: +# info["board"] = short_hit +# return True +# return False + + +def get_root() -> str: # sourcery skip: use-assigned-variable + "Determine the root folder of the device" + try: + c = os.getcwd() + except (OSError, AttributeError): + # unix port + c = "." + r = c + for r in [c, "/sd", "/flash", "/", "."]: + try: + _ = os.stat(r) + break + except OSError: + continue + return r + + +def file_exists(filename: str): + try: + if os.stat(filename)[0] >> 14: + return True + return False + except OSError: + return False + + +def show_help(): + # print("-p, --path path to store the stubs in, defaults to '.'") + sys.exit(1) + + +def read_path() -> str: + "get --path from cmdline. [unix/win]" + path = "" + if len(sys.argv) == 3: + cmd = (sys.argv[1]).lower() + if cmd in ("--path", "-p"): + path = sys.argv[2] + else: + show_help() + elif len(sys.argv) == 2: + show_help() + return path + + +def is_micropython() -> bool: + "runtime test to determine full or micropython" + # pylint: disable=unused-variable,eval-used + try: + # either test should fail on micropython + # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces + # Micropython : SyntaxError + # a = eval("1and 0") # lgtm [py/unused-local-variable] + # Eval blocks some minification aspects + + # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented + # Micropython: NotImplementedError + b = bytes("abc", encoding="utf8") # type: ignore # lgtm [py/unused-local-variable] + + # c) https://docs.micropython.org/en/latest/genrst/core_language.html#function-objects-do-not-have-the-module-attribute + # Micropython: AttributeError + c = is_micropython.__module__ # type: ignore # lgtm [py/unused-local-variable] + return False + except (NotImplementedError, AttributeError): + return True + + def main(): - D='lvgl' - try:import lvgl as A - except k:return - B=D - try:B='lvgl-{0}_{1}_{2}-{3}-{4}'.format(A.version_major(),A.version_minor(),A.version_patch(),A.version_info(),sys.platform) - except k:B='lvgl-{0}_{1}_{2}_{3}-{4}'.format(8,1,0,'dev',sys.platform) - finally:stubber=Stubber(firmware_id=B) - stubber.clean();stubber.modules=['io','lodepng','rtch',D];C.collect();stubber.create_all_stubs();stubber.report() -if __name__=='__main__'or g(): - try:x=logging.getLogger(t);logging.basicConfig(level=logging.INFO) - except j:pass - if not e('no_auto_stubber.txt'): - try:C.threshold(4*1024);C.enable() - except BaseException:pass - main() \ No newline at end of file + try: + import lvgl # type: ignore + except Exception: + # print("\n\nNOTE: The `lvgl` module could not be found on this firmware\n\n") + return + # Specify firmware name & version + fw_id = "lvgl" + try: + fw_id = "lvgl-{0}_{1}_{2}-{3}-{4}".format( + lvgl.version_major(), + lvgl.version_minor(), + lvgl.version_patch(), + lvgl.version_info(), + sys.platform, + ) + except Exception: + fw_id = "lvgl-{0}_{1}_{2}_{3}-{4}".format(8, 1, 0, "dev", sys.platform) + finally: + stubber = Stubber(firmware_id=fw_id) + stubber.clean() + # modules to stub : only lvgl specifics + stubber.modules = ["io", "lodepng", "rtch", "lvgl"] # spell-checker: enable + + gc.collect() + + stubber.create_all_stubs() + stubber.report() + + +if __name__ == "__main__" or is_micropython(): + try: + log = logging.getLogger("stubber") + logging.basicConfig(level=logging.INFO) + # logging.basicConfig(level=logging.DEBUG) + except NameError: + pass + if not file_exists("no_auto_stubber.txt"): + try: + gc.threshold(4 * 1024) # type: ignore + gc.enable() + except BaseException: + pass + main() diff --git a/mip/v5/createstubs_lvgl_mpy.mpy b/mip/v5/createstubs_lvgl_mpy.mpy index 20303bcbe..120b0c085 100644 Binary files a/mip/v5/createstubs_lvgl_mpy.mpy and b/mip/v5/createstubs_lvgl_mpy.mpy differ diff --git a/mip/v5/createstubs_mem.py b/mip/v5/createstubs_mem.py index edcf495ba..9a4349297 100644 --- a/mip/v5/createstubs_mem.py +++ b/mip/v5/createstubs_mem.py @@ -9,16 +9,19 @@ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device -This variant was generated from createstubs.py by micropython-stubber v1.16.0 +This variant was generated from createstubs.py by micropython-stubber v1.17.0 """ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -30,11 +33,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.0" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -42,24 +83,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -69,11 +107,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -92,20 +130,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -138,21 +179,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -167,10 +210,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -184,10 +227,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -197,21 +240,18 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") - self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True @@ -219,14 +259,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -234,11 +274,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -257,11 +302,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -275,7 +320,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -291,7 +336,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -301,36 +346,42 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -346,7 +397,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -364,41 +415,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -424,79 +487,83 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", - "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -518,13 +585,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -548,25 +616,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -619,10 +693,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -644,40 +714,40 @@ def main(): stubber.clean() # Read stubs from modulelist in the current folder or in /libs # fall back to default modules - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - _1 = gc.mem_free() # type: ignore - with open(p + "/modulelist.txt") as f: - print("Debug: List of modules: " + p + "/modulelist.txt") - line = f.readline() - while line: - line = line.strip() + def get_modulelist(stubber): + # new + gc.collect() + stubber.modules = [] # avoid duplicates + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + with open(fname) as f: + # print("DEBUG: list of modules: " + p + "/modulelist.txt") + while True: + line = f.readline().strip() + if not line: + break if len(line) > 0 and line[0] != "#": stubber.modules.append(line) - line = f.readline() - gc.collect() # type: ignore - print("Debug: Used memory to load modulelist.txt: " + str(_1 - gc.mem_free()) + " bytes") # type: ignore + gc.collect() + print("BREAK") break - except Exception: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - print("Could not find modulelist.txt, using default modules") + + if not stubber.modules: + stubber.modules = ["micropython"] + # _log.warn("Could not find modulelist.txt, using default modules") + gc.collect() + + stubber.modules = [] # avoid duplicates + get_modulelist(stubber) gc.collect() stubber.create_all_stubs() - stubber.report() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/mip/v5/createstubs_mem_min.py b/mip/v5/createstubs_mem_min.py index 03e5c7ade..9c2cfd272 100644 --- a/mip/v5/createstubs_mem_min.py +++ b/mip/v5/createstubs_mem_min.py @@ -1,281 +1,300 @@ -u='stubber' -t='{}/{}' -s='method' -r='function' -q='bool' -p='str' -o='float' -n='int' -m=NameError -l=sorted -k=NotImplementedError -b=',\n' -a='dict' -Z='list' -Y='tuple' -X='micropython' -W=str -V=repr -T='_' -S=KeyError -R=open -Q=IndexError -P=dir -O=ImportError -N=True -M='family' -L=len -K=print -J='board' -I='.' -H=AttributeError -A=False +x='No report file' +w='Failed to create the report.' +v='{}/{}' +u='method' +t='function' +s='bool' +r='str' +q='float' +p='int' +o='stubber' +n=TypeError +m=Exception +l=KeyError +k=sorted +j=NotImplementedError +e=',\n' +d='dict' +c='list' +b='tuple' +a='micropython' +Z=repr +W='-preview' +V='-' +U='board' +T=IndexError +S=print +R=True +Q='family' +P=len +O=open +N=ImportError +M=dir +K='port' +J='.' +I=AttributeError +H=False G='/' -E=None -D='version' F=OSError -C='' -import gc as B,os,sys -from ujson import dumps as c -try:from machine import reset -except O:pass -try:from collections import OrderedDict as d -except O:from ucollections import OrderedDict as d -__version__='v1.16.0' -v=2 -w=2 -e=[I,'/lib','/sd/lib','/flash/lib','lib'] +E=None +C='version' +B='' +import gc as D,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except N:pass +try:from collections import OrderedDict as f +except N:from ucollections import OrderedDict as f +__version__='v1.17.0' +y=2 +z=2 +A0=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(o) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(A,path=E,firmware_id=E): + def __init__(B,path=E,firmware_id=E): C=firmware_id try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise k('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();B.collect() - if C:A._fwid=C.lower() - elif A.info[M]==X:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=B.mem_free() + if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed') + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));D.collect() + if C:B._fwid=C.lower() + elif B.info[Q]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=D.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:f(path+G) - except F:K('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:X(path+G) + except F:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in P(I): - if A.startswith(T)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in M(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=V(type(E)).split("'")[1] - except Q:F=C - if F in{n,o,p,q,Y,Z,a}:G=1 - elif F in{r,s}:G=2 + E=getattr(H,A) + try:F=Z(type(E)).split("'")[1] + except T:F=B + if F in{p,q,r,s,b,c,d}:G=1 + elif F in{t,u}:G=2 elif F in'class':G=3 else:G=4 - D.append((A,V(E),V(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=l([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);B.collect();return D,J - def add_modules(A,modules):A.modules=l(set(A.modules)|set(modules)) - def create_all_stubs(A): - B.collect() - for C in A.modules:A.create_one_stub(C) + C.append((A,Z(E),Z(type(E)),E,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except MemoryError as J:S('MemoryError: {}'.format(J));sleep(1);reset() + C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);D.collect();return C,K + def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules)) + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();D.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') def create_one_stub(C,module_name): - D=module_name - if D in C.problematic:return A - if D in C.excluded:return A - H='{}/{}.py'.format(C.path,D.replace(I,G));B.collect();E=A - try:E=C.create_module_stub(D,H) - except F:return A - B.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:M=D.replace(I,T)+'.py';H=J.path+G+M - else:M=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');U=B.mem_free() - except O:return A - f(H) - with R(H,'w')as L:P=W(J.info).replace('OrderedDict(',C).replace('})','}');Q='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);L.write(Q);L.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(L,K,D,C) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del K - except(F,S):pass - try:del sys.modules[D] - except S:pass - B.collect();return N - def write_object_stub(M,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';Q=in_class;P=object_expr;O='Exception';H=fp;D=indent;B.collect() - if P in M.problematic:return - R,N=M.get_obj_attributes(P) - if N:K(N) - for(E,J,G,T,f)in R: - if E in['classmethod','staticmethod','BaseException',O]:continue - if E[0].isdigit():continue - if G==""and L(D)<=w*4: - U=C;V=E.endswith(O)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=O - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);M.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',Q+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[s,r,'closure']): - W=b;X=C - if Q>0:X='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,W) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,X,W) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith("5}'.format(C,L,Q)) + except N:return H + X(I) + with O(I,'w')as P:S=str(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,M,C,B) + K.report_add(C,I) + if C not in{'os','sys','logging','gc'}: + try:del M + except(F,l):A.warning('could not del new_module') + D.collect();return R + def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): + X='generator';W='{0}{1}: {3} = {2}\n';V='bound_method';U='Incomplete';N=in_class;M='Exception';L=object_expr;I=fp;E=indent;D.collect() + if L in K.problematic:A.warning('SKIPPING problematic module:{}'.format(L));return + Y,O=K.get_obj_attributes(L) + if O:A.error(O) + for(F,J,H,Z,e)in Y: + if F in['classmethod','staticmethod','BaseException',M]:continue + if F[0].isdigit():A.warning('NameError: invalid name {}'.format(F));continue + if H==""and P(E)<=z*4: + Q=B;R=F.endswith(M)or F.endswith('Error')or F in['KeyboardInterrupt','StopIteration','SystemExit'] + if R:Q=M + C='\n{}class {}({}):\n'.format(E,F,Q) + if R:C+=E+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,F),E+' ',N+1);C=E+' def __init__(self, *argv, **kwargs) -> None:\n';C+=E+' ...\n\n';I.write(C) + elif any(A in H for A in[u,t,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(E)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(E,F,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(E,F,T,S) + C+=E+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=C - if A[D]==C and sys.platform not in('unix','win32'): - try:l=os.uname();A[D]=l.release - except(Q,H,TypeError):pass - for(m,n,o)in[(i,i,'const'),(j,j,'FAT'),(k,'pybricks.hubs','EV3Brick')]: - try:p=__import__(n,E,E,o);A[M]=m;del p;break - except(O,S):pass - if A[M]==k:A['release']='2.0.0' - if A[M]==X: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - R=int(A[G]);Z=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][R>>10] - if Z:A[c]=Z - A[G]='v{}.{}'.format(R&255,R>>8&3) - A[a]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def g(info,board_descr,filename): - with R(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return N - return A + if'uname'in M(os): + A[D]=Y(os.uname()[3]) + if not A[D]:A[D]=Y(os.uname()[2]) + elif C in M(sys):A[D]=Y(sys.version) + except(I,T,n):pass + if A[C]==B and sys.platform not in(S,R): + try:b=os.uname();A[C]=b.release + except(T,I,n):pass + for(c,d,e)in[(V,V,'const'),(X,X,'FAT'),(Z,'pybricks.hubs','EV3Brick')]: + try:g=__import__(d,E,E,e);A[Q]=c;del g;break + except(N,l):pass + if A[Q]==Z:A['release']='2.0.0' + if A[Q]==a: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[P]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[D]and not A[C].endswith(W):A[C]=A[C]+W + A[L]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A +def A1(version): + A=version;B=J.join([str(A)for A in A[:3]]) + if P(A)>3 and A[3]:B+=V+A[3] + return B +def A2(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except N:A.warning('BOARDNAME not found');C=B + return C def get_root(): try:A=os.getcwd() - except(F,H):A=I + except(F,I):A=J B=A - for B in[A,'/sd','/flash',G,I]: + for B in[A,'/sd','/flash',G,J]: try:C=os.stat(B);break except F:continue return B -def h(filename): +def g(filename): try: - if os.stat(filename)[0]>>14:return N - return A - except F:return A -def i():sys.exit(1) + if os.stat(filename)[0]>>14:return R + return H + except F:return H +def h():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): - path=C - if L(sys.argv)==3: + path=B + if P(sys.argv)==3: A=sys.argv[1].lower() if A in('--path','-p'):path=sys.argv[2] - else:i() - elif L(sys.argv)==2:i() + else:h() + elif P(sys.argv)==2:h() return path -def j(): - try:B=bytes('abc',encoding='utf8');C=j.__module__;return A - except(k,H):return N +def i(): + try:A=bytes('abc',encoding='utf8');B=i.__module__;return H + except(j,I):return R def main(): - E='/modulelist.txt';stubber=Stubber(path=read_path());stubber.clean();stubber.modules=[] - for C in e: - try: - F=B.mem_free() - with R(C+E)as D: - K('Debug: List of modules: '+C+E);A=D.readline() - while A: - A=A.strip() - if L(A)>0 and A[0]!='#':stubber.modules.append(A) - A=D.readline() - B.collect();K('Debug: Used memory to load modulelist.txt: '+W(F-B.mem_free())+' bytes');break - except Exception:pass - if not stubber.modules:stubber.modules=[X] - B.collect();stubber.create_all_stubs();stubber.report() -if __name__=='__main__'or j(): - try:x=logging.getLogger(u);logging.basicConfig(level=logging.INFO) - except m:pass - if not h('no_auto_stubber.txt'): - try:B.threshold(4*1024);B.enable() + stubber=Stubber(path=read_path());stubber.clean() + def A(stubber): + D.collect();stubber.modules=[] + for C in A0: + B=C+'/modulelist.txt' + if not g(B):continue + with O(B)as E: + while R: + A=E.readline().strip() + if not A:break + if P(A)>0 and A[0]!='#':stubber.modules.append(A) + D.collect();S('BREAK');break + if not stubber.modules:stubber.modules=[a] + D.collect() + stubber.modules=[];A(stubber);D.collect();stubber.create_all_stubs() +if __name__=='__main__'or i(): + if not g('no_auto_stubber.txt'): + try:D.threshold(4*1024);D.enable() except BaseException:pass main() \ No newline at end of file diff --git a/mip/v5/createstubs_mem_mpy.mpy b/mip/v5/createstubs_mem_mpy.mpy index 16401c210..f60d383c7 100644 Binary files a/mip/v5/createstubs_mem_mpy.mpy and b/mip/v5/createstubs_mem_mpy.mpy differ diff --git a/mip/v5/createstubs_min.py b/mip/v5/createstubs_min.py index db0387e80..63ab05685 100644 --- a/mip/v5/createstubs_min.py +++ b/mip/v5/createstubs_min.py @@ -1,254 +1,274 @@ -w='pyb' -v='stubber' -u='{}/{}' -t='logging' -s='sys' -r='method' -q='function' -p='bool' -o='str' -n='float' -m='int' -l=NameError +z='No report file' +y='Failed to create the report.' +x='{}/{}' +w='logging' +v='sys' +u='method' +t='function' +s='bool' +r='str' +q='float' +p='int' +o='stubber' +n=TypeError +m=Exception +l=KeyError k=sorted j=NotImplementedError -b='pycom' -a=',\n' -Z='dict' -Y='list' -X='tuple' -W='micropython' -V=open -U=repr -S='_' +f='pycom' +e=',\n' +d='dict' +c='list' +b='tuple' +a='micropython' +Z=repr +Y=print +V='-preview' +U=True +T='-' +S='board' R=len -Q=KeyError +Q=open P=IndexError -O=dir -N=print -M=ImportError -L=True -K='family' -J='board' -I='.' -H=AttributeError -A=False +O='family' +N=ImportError +M=dir +K='port' +J='.' +I=AttributeError +H=False G='/' -E=None -F=OSError -D='version' +E=OSError +D=None +C='version' B='' -import gc as C,os,sys -from ujson import dumps as c -try:from machine import reset -except M:pass -try:from collections import OrderedDict as d -except M:from ucollections import OrderedDict as d -__version__='v1.16.0' -x=2 -y=2 -z=[I,'/lib','/sd/lib','/flash/lib','lib'] +import gc as F,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except N:pass +try:from collections import OrderedDict as g +except N:from ucollections import OrderedDict as g +__version__='v1.17.0' +A0=2 +A1=2 +A5=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=Y + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(o) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(A,path=E,firmware_id=E): - B=firmware_id + def __init__(B,path=D,firmware_id=D): + C=firmware_id try: if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();C.collect() - if B:A._fwid=B.lower() - elif A.info[K]==W:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=C.mem_free() + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[S]));F.collect() + if C:B._fwid=C.lower() + elif B.info[O]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(T) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=F.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:e(path+G) - except F:N('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:W(path+G) + except E:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in O(I): - if A.startswith(S)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in M(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=U(type(E)).split("'")[1] - except P:F=B - if F in{m,n,o,p,X,Y,Z}:G=1 - elif F in{q,r}:G=2 - elif F in'class':G=3 + D=getattr(H,A) + try:E=Z(type(D)).split("'")[1] + except P:E=B + if E in{p,q,r,s,b,c,d}:G=1 + elif E in{t,u}:G=2 + elif E in'class':G=3 else:G=4 - D.append((A,U(E),U(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=k([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);C.collect();return D,J + C.append((A,Z(D),Z(type(D)),D,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except MemoryError as J:Y('MemoryError: {}'.format(J));sleep(1);reset() + C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules)) - def create_all_stubs(A): - C.collect() - for B in A.modules:A.create_one_stub(B) - def create_one_stub(B,module_name): - D=module_name - if D in B.problematic:return A - if D in B.excluded:return A - H='{}/{}.py'.format(B.path,D.replace(I,G));C.collect();E=A - try:E=B.create_module_stub(D,H) - except F:return A - C.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(I,S)+'.py';H=J.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');T=C.mem_free() - except M:return A - e(H) - with V(H,'w')as N:P=str(J.info).replace('OrderedDict(',B).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(N,K,D,B) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os',s,t,'gc'}: - try:del K - except(F,Q):pass - try:del sys.modules[D] - except Q:pass - C.collect();return L + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') + def create_one_stub(C,module_name): + B=module_name + if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H + if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H + I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();D=H + try:D=C.create_module_stub(B,I) + except E:return H + F.collect();return D + def create_module_stub(K,module_name,file_name=D): + I=file_name;C=module_name + if I is D:L=C.replace(J,'_')+'.pyi';I=K.path+G+L + else:L=I.split(G)[-1] + if G in C:C=C.replace(G,J) + M=D + try:M=__import__(C,D,D,'*');P=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,P)) + except N:return H + W(I) + 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) + K.report_add(C,I) + if C not in{'os',v,w,'gc'}: + try:del M + except(E,l):A.warning('could not del new_module') + F.collect();return U def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';P=in_class;O=object_expr;M='Exception';H=fp;D=indent;C.collect() - if O in K.problematic:return - S,L=K.get_obj_attributes(O) - if L:N(L) - for(E,J,G,T,f)in S: + X='generator';W='{0}{1}: {3} = {2}\n';V='bound_method';U='Incomplete';N=in_class;M='Exception';L=object_expr;I=fp;D=indent;F.collect() + if L in K.problematic:A.warning('SKIPPING problematic module:{}'.format(L));return + Y,O=K.get_obj_attributes(L) + if O:A.error(O) + for(E,J,H,Z,e)in Y: if E in['classmethod','staticmethod','BaseException',M]:continue - if E[0].isdigit():continue - if G==""and R(D)<=y*4: - U=B;V=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=M - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',P+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[r,q,'closure']): - W=b;a=B - if P>0:a='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,W) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,a,W) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith(""and R(D)<=A1*4: + P=B;Q=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] + if Q:P=M + C='\n{}class {}({}):\n'.format(D,E,P) + if Q:C+=D+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C) + elif any(A in H for A in[u,t,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S) + C+=D+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=B - if A[D]==B and sys.platform not in('unix','win32'): - try:j=os.uname();A[D]=j.release - except(P,H,TypeError):pass - for(k,l,m)in[(h,h,'const'),(b,b,'FAT'),(i,'pybricks.hubs','EV3Brick')]: - try:n=__import__(l,E,E,m);A[K]=k;del n;break - except(M,Q):pass - if A[K]==i:A['release']='2.0.0' - if A[K]==W: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - U=int(A[G]);Y=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][U>>10] - if Y:A[c]=Y - A[G]='v{}.{}'.format(U&255,U>>8&3) - A[Z]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def f(info,board_descr,filename): - with V(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return L - return A + if'uname'in M(os): + A[E]=X(os.uname()[3]) + if not A[E]:A[E]=X(os.uname()[2]) + elif C in M(sys):A[E]=X(sys.version) + except(I,P,n):pass + if A[C]==B and sys.platform not in(U,T): + try:Z=os.uname();A[C]=Z.release + except(P,I,n):pass + for(b,c,d)in[(W,W,'const'),(f,f,'FAT'),(Y,'pybricks.hubs','EV3Brick')]: + try:e=__import__(c,D,D,d);A[O]=b;del e;break + except(N,l):pass + if A[O]==Y:A['release']='2.0.0' + if A[O]==a: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[D,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[R]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[E]and not A[C].endswith(V):A[C]=A[C]+V + A[L]=f"{A[C]}-{A[E]}"if A[E]else f"{A[C]}";return A +def A2(version): + A=version;B=J.join([str(A)for A in A[:3]]) + if R(A)>3 and A[3]:B+=T+A[3] + return B +def A3(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except N:A.warning('BOARDNAME not found');C=B + return C def get_root(): try:A=os.getcwd() - except(F,H):A=I + except(E,I):A=J B=A - for B in[A,'/sd','/flash',G,I]: + for B in[A,'/sd','/flash',G,J]: try:C=os.stat(B);break - except F:continue + except E:continue return B -def g(filename): +def A4(filename): try: - if os.stat(filename)[0]>>14:return L - return A - except F:return A -def h():sys.exit(1) + if os.stat(filename)[0]>>14:return U + return H + except E:return H +def h():Y("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): path=B if R(sys.argv)==3: @@ -258,13 +278,11 @@ def read_path(): elif R(sys.argv)==2:h() return path def i(): - try:B=bytes('abc',encoding='utf8');C=i.__module__;return A - except(j,H):return L -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','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','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',t,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',W,'mip','mip/__init__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform',w,b,'pye','qrcode','queue','random','requests','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',s,'time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',D,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];C.collect();stubber.create_all_stubs();stubber.report() + try:A=bytes('abc',encoding='utf8');B=i.__module__;return H + except(j,I):return U +def main():stubber=Stubber(path=read_path());stubber.clean();stubber.modules=['WM8960','_OTA','_asyncio','_boot_fat','_coap','_espnow','_flash_control_OTA','_main_pybytes','_mqtt','_mqtt_core','_msg_handl','_onewire','_periodical_pin','_pybytes','_pybytes_ca','_pybytes_config','_pybytes_config_reader','_pybytes_connection','_pybytes_constants','_pybytes_debug','_pybytes_library','_pybytes_machine_learning','_pybytes_main','_pybytes_protocol','_pybytes_pyconfig','_pybytes_pymesh_config','_rp2','_terminal','_thread','_uasyncio','_urequest','adcfft','aioble/__init__','aioble/central','aioble/client','aioble/core','aioble/device','aioble/l2cap','aioble/peripheral','aioble/security','aioble/server','aioespnow','ak8963','apa102','apa106','argparse','array','asyncio/__init__','asyncio/core','asyncio/event','asyncio/funcs','asyncio/lock','asyncio/stream','binascii','bluetooth','breakout_as7262','breakout_bh1745','breakout_bme280','breakout_bme68x','breakout_bmp280','breakout_dotmatrix','breakout_encoder','breakout_icp10125','breakout_ioexpander','breakout_ltr559','breakout_matrix11x7','breakout_mics6814','breakout_msa301','breakout_paa5100','breakout_pmw3901','breakout_potentiometer','breakout_rgbmatrix5x5','breakout_rtc','breakout_scd41','breakout_sgp30','breakout_trackball','breakout_vl53l5cx','btree','cmath','collections','crypto','cryptolib','curl','deflate','dht','display','display_driver_utils','ds18x20','encoder','errno','esp','esp32','espidf','espnow','ffi','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',w,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',a,'mip','mip/__init__','mip/__main__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform','pyb',f,'pye','qrcode','queue','random','requests','requests/__init__','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',v,'termios','time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',C,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];F.collect();stubber.create_all_stubs() if __name__=='__main__'or i(): - try:A0=logging.getLogger(v);logging.basicConfig(level=logging.INFO) - except l:pass - if not g('no_auto_stubber.txt'): - try:C.threshold(4*1024);C.enable() + if not A4('no_auto_stubber.txt'): + try:F.threshold(4*1024);F.enable() except BaseException:pass main() \ No newline at end of file diff --git a/mip/v5/createstubs_mpy.mpy b/mip/v5/createstubs_mpy.mpy index 28374dcc1..5f0dcebeb 100644 Binary files a/mip/v5/createstubs_mpy.mpy and b/mip/v5/createstubs_mpy.mpy differ diff --git a/mip/v5/modulelist.txt b/mip/v5/modulelist.txt index 16a92863c..32dacba0f 100644 --- a/mip/v5/modulelist.txt +++ b/mip/v5/modulelist.txt @@ -1,5 +1,4 @@ -# list of modules to stub. -# used by the memory optimised createstubs_mem.py +# list of modules to stub used by the memory optimised createstubs_mem.py WM8960 _OTA _asyncio @@ -45,6 +44,7 @@ aioespnow ak8963 apa102 apa106 +argparse array asyncio/__init__ asyncio/core @@ -93,6 +93,7 @@ esp esp32 espidf espnow +ffi flashbdev framebuf freesans20 @@ -129,6 +130,7 @@ microWebTemplate micropython mip mip/__init__ +mip/__main__ motor mpu6500 mpu9250 @@ -156,6 +158,7 @@ qrcode queue random requests +requests/__init__ rp2 rtch samd @@ -168,6 +171,7 @@ ssl stm struct sys +termios time tpcalib uarray diff --git a/mip/v6/createstubs.py b/mip/v6/createstubs.py index 8e4714a8c..2ab22f8ed 100644 --- a/mip/v6/createstubs.py +++ b/mip/v6/createstubs.py @@ -4,11 +4,14 @@ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -20,11 +23,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.0" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -32,24 +73,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -59,11 +97,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -82,20 +120,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -110,11 +151,7 @@ def get_obj_attributes(self, item_instance: object): order = 4 _result.append((name, repr(val), repr(type(val)), val, order)) except AttributeError as e: - _errors.append( - "Couldn't get attribute '{}' from object '{}', Err: {}".format( - name, item_instance, e - ) - ) + _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e)) except MemoryError as e: print("MemoryError: {}".format(e)) sleep(1) @@ -132,21 +169,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -161,10 +200,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -178,12 +217,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info( - "Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1) - ) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -195,40 +232,35 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: module_name, self._fwid, info_, __version__ ) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write( + "from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n" + ) self.write_object_stub(fp, new_module, module_name, "") - self._report.append( - '{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/")) - ) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True - def write_object_stub( - self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0 - ): + def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0): "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -236,11 +268,16 @@ def write_object_stub( # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -259,11 +296,11 @@ def write_object_stub( if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -277,11 +314,7 @@ def write_object_stub( s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug( - "# def {1} function/method/closure, type = '{0}'".format( - item_type_txt, item_name - ) - ) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -292,16 +325,14 @@ def write_object_stub( first = "self, " # class method - add function decoration if "bound_method" in item_type_txt or "bound_method" in item_repr: - s = "{}@classmethod\n".format( - indent - ) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret) - else: - s = "{}def {}({}*args, **kwargs) -> {}:\n".format( - indent, item_name, first, ret + s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format( + indent, item_name, ret ) + else: + s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -311,38 +342,42 @@ def write_object_stub( t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format( - indent, item_name, t, item_type_txt, item_repr - ) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -358,7 +393,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -376,45 +411,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info( - "Created stubs for {} modules on board {}\nPath: {}".format( - len(self._report), self._fwid, self.path - ) - ) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -440,85 +483,85 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", - "port": "stm32" - if sys.platform.startswith("pyb") - else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = ( - sys.implementation._machine - if "_machine" in dir(sys.implementation) - else os.uname().machine + _machine = ( + sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore ) - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -540,14 +583,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") - and info["version"] - >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -571,25 +614,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -642,10 +691,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -715,6 +760,7 @@ def main(): "ak8963", "apa102", "apa106", + "argparse", "array", "asyncio/__init__", "asyncio/core", @@ -763,6 +809,7 @@ def main(): "esp32", "espidf", "espnow", + "ffi", "flashbdev", "framebuf", "freesans20", @@ -799,6 +846,7 @@ def main(): "micropython", "mip", "mip/__init__", + "mip/__main__", "motor", "mpu6500", "mpu9250", @@ -826,6 +874,7 @@ def main(): "queue", "random", "requests", + "requests/__init__", "rp2", "rtch", "samd", @@ -838,6 +887,7 @@ def main(): "stm", "struct", "sys", + "termios", "time", "tpcalib", "uarray", @@ -905,16 +955,9 @@ def main(): gc.collect() stubber.create_all_stubs() - stubber.report() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/mip/v6/createstubs_db.py b/mip/v6/createstubs_db.py index 0c7225a05..38f14d899 100644 --- a/mip/v6/createstubs_db.py +++ b/mip/v6/createstubs_db.py @@ -18,16 +18,19 @@ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device -This variant was generated from createstubs.py by micropython-stubber v1.16.0 +This variant was generated from createstubs.py by micropython-stubber v1.17.0 """ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -39,11 +42,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.0" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -51,24 +92,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -78,11 +116,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -101,20 +139,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -147,21 +188,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -176,10 +219,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -193,10 +236,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -206,21 +249,18 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") - self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True @@ -228,14 +268,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -243,11 +283,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -266,11 +311,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -284,7 +329,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -300,7 +345,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -310,36 +355,42 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -355,7 +406,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -373,41 +424,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -433,79 +496,83 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", - "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -527,13 +594,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -557,25 +625,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -628,10 +702,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -645,92 +715,99 @@ def is_micropython() -> bool: return True -def main(): - import machine # type: ignore +SKIP_FILE = "modulelist.done" + +def get_modules(skip=0): + # new + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + try: + with open(fname) as f: + i = 0 + while True: + line = f.readline().strip() + if not line: + break + if len(line) > 0 and line[0] == "#": + continue + i += 1 + if i < skip: + continue + yield line + break + except OSError: + pass + + +def write_skip(done): + # write count of modules already processed to file + with open(SKIP_FILE, "w") as f: + f.write(str(done) + "\n") + + +def read_skip(): + # read count of modules already processed from file + done = 0 try: - f = open("modulelist.done", "r+b") - was_running = True - print("Opened existing db") + with open(SKIP_FILE) as f: + done = int(f.readline().strip()) except OSError: - f = open("modulelist.done", "w+b") - print("created new db") - was_running = False + pass + return done + + +def main(): + import machine # type: ignore + + was_running = file_exists(SKIP_FILE) + if was_running: + log.info("Continue from last run") + else: + log.info("Starting new run") + # try: + # f = open("modulelist.done", "r+b") + # was_running = True + # print("Continue from last run") + # except OSError: + # f = open("modulelist.done", "w+b") + # was_running = False stubber = Stubber(path=read_path()) # f_name = "{}/{}".format(stubber.path, "modules.json") + skip = 0 if not was_running: # Only clean folder if this is a first run stubber.clean() - # get list of modules to process - get_modulelist(stubber) - # remove the ones that are already done - modules_done = {} # type: dict[str, str] - try: - with open("modulelist.done") as f: - # not optimal , but works on mpremote and esp8266 - for line in f.read().split("\n"): - line = line.strip() - gc.collect() - if len(line) > 0: - key, value = line.split("=", 1) - modules_done[key] = value - except (OSError, SyntaxError): - pass - gc.collect() - # see if we can continue from where we left off - modules = [m for m in stubber.modules if m not in modules_done.keys()] - gc.collect() - for modulename in modules: + stubber.report_start("modules.json") + else: + skip = read_skip() + stubber._json_name = "{}/{}".format(stubber.path, "modules.json") + + for modulename in get_modules(skip): # ------------------------------------ # do epic shit # but sometimes things fail / run out of memory and reboot - ok = False try: - ok = stubber.create_one_stub(modulename) + stubber.create_one_stub(modulename) except MemoryError: # RESET AND HOPE THAT IN THE NEXT CYCLE WE PROGRESS FURTHER machine.reset() # ------------------------------------- gc.collect() - modules_done[modulename] = str(stubber._report[-1] if ok else "failed") - with open("modulelist.done", "a") as f: - f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + # modules_done[modulename] = str(stubber._report[-1] if ok else "failed") + # with open("modulelist.done", "a") as f: + # f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + skip += 1 + write_skip(skip) - # Finished processing - load all the results , and remove the failed ones - if modules_done: - # stubber.write_json_end(mod_fp) - stubber._report = [v for _, v in modules_done.items() if v != "failed"] - stubber.report() - - -def get_modulelist(stubber): - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - with open(p + "/modulelist.txt") as f: - print("DEBUG: list of modules: " + p + "/modulelist.txt") - for line in f.read().split("\n"): - line = line.strip() - if len(line) > 0 and line[0] != "#": - stubber.modules.append(line) - gc.collect() - break - except OSError: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - _log.warn("Could not find modulelist.txt, using default modules") - gc.collect() + print("All modules have been processed, Finalizing report") + stubber.report_end() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/mip/v6/createstubs_db_min.py b/mip/v6/createstubs_db_min.py index 3e0a7370d..f3bc2d297 100644 --- a/mip/v6/createstubs_db_min.py +++ b/mip/v6/createstubs_db_min.py @@ -1,301 +1,325 @@ -v='stubber' -u='{}/{}' -t='method' -s='function' -r='bool' -q='str' -p='float' -o='int' -n=NameError -m=sorted -l=MemoryError -k=NotImplementedError -b=',\n' -a='dict' -Z='list' -Y='tuple' -X='micropython' -W=str -V=repr -T='_' -S=KeyError -R=IndexError -Q=dir -P=ImportError -O='family' -N=print -M=True -L=len -K='board' +A2='No report file' +A1='Failed to create the report.' +A0='method' +z='function' +y='bool' +x='str' +w='float' +v='int' +u='micropython' +t='stubber' +s=TypeError +r=Exception +q=KeyError +p=sorted +o=MemoryError +n=NotImplementedError +j=',\n' +i='modules.json' +h='{}/{}' +g='w' +f='dict' +e='list' +d='tuple' +c=str +b=repr +W='-preview' +V='-' +U='board' +T=IndexError +S=print +R=True +Q='family' +P=len +O=ImportError +N=dir +M=open +K='port' J='.' -I=open -H=AttributeError +I=AttributeError +H=False G='/' -F=False E=None -D='version' -A=OSError -C='' -import gc as B,os,sys -from ujson import dumps as c -try:from machine import reset -except P:pass -try:from collections import OrderedDict as d -except P:from ucollections import OrderedDict as d -__version__='v1.16.0' -w=2 -x=2 -e=[J,'/lib','/sd/lib','/flash/lib','lib'] +D=OSError +C='version' +B='' +import gc as F,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except O:pass +try:from collections import OrderedDict as k +except O:from ucollections import OrderedDict as k +__version__='v1.17.0' +A3=2 +A4=2 +A5=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(t) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(C,path=E,firmware_id=E): - D=firmware_id + def __init__(B,path=E,firmware_id=E): + C=firmware_id try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise k('MicroPython 1.13.0 cannot be stubbed') - except H:pass - C._report=[];C.info=_info();B.collect() - if D:C._fwid=D.lower() - elif C.info[O]==X:C._fwid='{family}-{ver}-{port}-{board}'.format(**C.info) - else:C._fwid='{family}-{ver}-{port}'.format(**C.info) - C._start_free=B.mem_free() + if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise n('MicroPython 1.13.0 cannot be stubbed') + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));F.collect() + if C:B._fwid=C.lower() + elif B.info[Q]==u:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=F.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - C.path='{}/stubs/{}'.format(path,C.flat_fwid).replace('//',G) - try:f(path+G) - except A:N('error creating stub folder {}'.format(path)) - C.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];C.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];C.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:X(path+G) + except D:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in Q(I): - if A.startswith(T)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in N(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=V(type(E)).split("'")[1] - except R:F=C - if F in{o,p,q,r,Y,Z,a}:G=1 - elif F in{s,t}:G=2 - elif F in'class':G=3 + D=getattr(H,A) + try:E=b(type(D)).split("'")[1] + except T:E=B + if E in{v,w,x,y,d,e,f}:G=1 + elif E in{z,A0}:G=2 + elif E in'class':G=3 else:G=4 - D.append((A,V(E),V(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except l as K:sleep(1);reset() - D=m([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);B.collect();return D,J - def add_modules(A,modules):A.modules=m(set(A.modules)|set(modules)) - def create_all_stubs(A): - B.collect() - for C in A.modules:A.create_one_stub(C) + C.append((A,b(D),b(type(D)),D,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except o as J:S('MemoryError: {}'.format(J));sleep(1);reset() + C=p([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K + def add_modules(A,modules):A.modules=p(set(A.modules)|set(modules)) + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') def create_one_stub(C,module_name): - D=module_name - if D in C.problematic:return F - if D in C.excluded:return F - H='{}/{}.py'.format(C.path,D.replace(J,G));B.collect();E=F - try:E=C.create_module_stub(D,H) - except A:return F - B.collect();return E + B=module_name + if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H + if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H + I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();E=H + try:E=C.create_module_stub(B,I) + except D:return H + F.collect();return E def create_module_stub(K,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(J,T)+'.py';H=K.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,J) - L=E - try:L=__import__(D,E,E,'*');U=B.mem_free() - except P:return F - f(H) - with I(H,'w')as N:Q=W(K.info).replace('OrderedDict(',C).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,K._fwid,Q,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');K.write_object_stub(N,L,D,C) - K._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del L - except(A,S):pass - try:del sys.modules[D] - except S:pass - B.collect();return M + I=file_name;C=module_name + if I is E:L=C.replace(J,'_')+'.pyi';I=K.path+G+L + else:L=I.split(G)[-1] + if G in C:C=C.replace(G,J) + N=E + try:N=__import__(C,E,E,'*');Q=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,Q)) + except O:return H + X(I) + with M(I,g)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) + K.report_add(C,I) + if C not in{'os','sys','logging','gc'}: + try:del N + except(D,q):A.warning('could not del new_module') + F.collect();return R def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';Q=in_class;P=object_expr;O='Exception';H=fp;E=indent;B.collect() - if P in K.problematic:return - R,M=K.get_obj_attributes(P) - if M:N(M) - for(F,J,G,T,f)in R: - if F in['classmethod','staticmethod','BaseException',O]:continue - if F[0].isdigit():continue - if G==""and L(E)<=x*4: - U=C;V=F.endswith(O)or F.endswith('Error')or F in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=O - D='\n{}class {}({}):\n'.format(E,F,U) - if V:D+=E+' ...\n';H.write(D);return - H.write(D);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,F),E+' ',Q+1);D=E+' def __init__(self, *argv, **kwargs) -> None:\n';D+=E+' ...\n\n';H.write(D) - elif any(A in G for A in[t,s,'closure']): - W=b;X=C - if Q>0:X='self, ' - if c in G or c in J:D='{}@classmethod\n'.format(E)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(E,F,W) - else:D='{}def {}({}*args, **kwargs) -> {}:\n'.format(E,F,X,W) - D+=E+' ...\n\n';H.write(D) - elif G=="":0 - elif G.startswith(""and P(D)<=A4*4: + Q=B;R=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] + if R:Q=M + C='\n{}class {}({}):\n'.format(D,E,Q) + if R:C+=D+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C) + elif any(A in H for A in[A0,z,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S) + C+=D+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=C - if A[D]==C and sys.platform not in('unix','win32'): - try:l=os.uname();A[D]=l.release - except(R,H,TypeError):pass - for(m,n,o)in[(i,i,'const'),(j,j,'FAT'),(k,'pybricks.hubs','EV3Brick')]: - try:p=__import__(n,E,E,o);A[O]=m;del p;break - except(P,S):pass - if A[O]==k:A['release']='2.0.0' - if A[O]==X: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - N=int(A[G]);Z=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][N>>10] - if Z:A[c]=Z - A[G]='v{}.{}'.format(N&255,N>>8&3) - A[a]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def g(info,board_descr,filename): - with I(filename,'r')as B: - while 1: - A=B.readline() - if not A:break - C,D=A.split(',')[0].strip(),A.split(',')[1].strip() - if C==board_descr:info[K]=D;return M - return F -def get_root(): - try:B=os.getcwd() - except(A,H):B=J - C=B - for C in[B,'/sd','/flash',G,J]: - try:D=os.stat(C);break - except A:continue + if'uname'in N(os): + A[D]=Y(os.uname()[3]) + if not A[D]:A[D]=Y(os.uname()[2]) + elif C in N(sys):A[D]=Y(sys.version) + except(I,T,s):pass + if A[C]==B and sys.platform not in(S,R): + try:a=os.uname();A[C]=a.release + except(T,I,s):pass + for(b,c,d)in[(V,V,'const'),(X,X,'FAT'),(Z,'pybricks.hubs','EV3Brick')]: + try:e=__import__(c,E,E,d);A[Q]=b;del e;break + except(O,q):pass + if A[Q]==Z:A['release']='2.0.0' + if A[Q]==u: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[P]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[D]and not A[C].endswith(W):A[C]=A[C]+W + A[L]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A +def A6(version): + A=version;B=J.join([c(A)for A in A[:3]]) + if P(A)>3 and A[3]:B+=V+A[3] + return B +def A7(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except O:A.warning('BOARDNAME not found');C=B return C -def h(filename): +def get_root(): + try:A=os.getcwd() + except(D,I):A=J + B=A + for B in[A,'/sd','/flash',G,J]: + try:C=os.stat(B);break + except D:continue + return B +def Z(filename): try: - if os.stat(filename)[0]>>14:return M - return F - except A:return F -def i():sys.exit(1) + if os.stat(filename)[0]>>14:return R + return H + except D:return H +def l():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): - path=C - if L(sys.argv)==3: + path=B + if P(sys.argv)==3: A=sys.argv[1].lower() if A in('--path','-p'):path=sys.argv[2] - else:i() - elif L(sys.argv)==2:i() + else:l() + elif P(sys.argv)==2:l() return path -def j(): - try:A=bytes('abc',encoding='utf8');B=j.__module__;return F - except(k,H):return M -def main(): - K='failed';G='modulelist.done';import machine as O - try:C=I(G,'r+b');N=M - except A:C=I(G,'w+b');N=F - stubber=Stubber(path=read_path()) - if not N:stubber.clean() - y(stubber);D={} - try: - with I(G)as C: - for E in C.read().split('\n'): - E=E.strip();B.collect() - if L(E)>0:P,Q=E.split('=',1);D[P]=Q - except(A,SyntaxError):pass - B.collect();R=[A for A in stubber.modules if A not in D.keys()];B.collect() - for H in R: - J=F - try:J=stubber.create_one_stub(H) - except l:O.reset() - B.collect();D[H]=W(stubber._report[-1]if J else K) - with I(G,'a')as C:C.write('{}={}\n'.format(H,'ok'if J else K)) - if D:stubber._report=[A for(B,A)in D.items()if A!=K];stubber.report() -def y(stubber): - E='/modulelist.txt';stubber.modules=[] - for D in e: +def m(): + try:A=bytes('abc',encoding='utf8');B=m.__module__;return H + except(n,I):return R +a='modulelist.done' +def A8(skip=0): + for E in A5: + B=E+'/modulelist.txt' + if not Z(B):continue try: - with I(D+E)as F: - N('DEBUG: list of modules: '+D+E) - for C in F.read().split('\n'): - C=C.strip() - if L(C)>0 and C[0]!='#':stubber.modules.append(C) - B.collect();break - except A:pass - if not stubber.modules:stubber.modules=[X];_log.warn('Could not find modulelist.txt, using default modules') - B.collect() -if __name__=='__main__'or j(): - try:z=logging.getLogger(v);logging.basicConfig(level=logging.INFO) - except n:pass - if not h('no_auto_stubber.txt'): - try:B.threshold(4*1024);B.enable() + with M(B)as F: + C=0 + while R: + A=F.readline().strip() + if not A:break + if P(A)>0 and A[0]=='#':continue + C+=1 + if C bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) @@ -202,10 +223,11 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + # lets not try - most times it does not work anyway + # try: + # del sys.modules[module_name] + # except KeyError: + # self.log.warning("could not del sys.modules[{}]".format(module_name)) gc.collect() return True @@ -231,8 +253,13 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, self.log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + self.log.info("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -251,7 +278,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods @@ -304,12 +331,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ["object", "set", "frozenset", "Pin", "FileIO"]: # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + s = "{0}{1} : {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) self.log.debug("\n" + s) else: @@ -319,12 +348,12 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -338,6 +367,7 @@ def flat_fwid(self): def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" + wdt.feed() if path is None: path = self.path self.log.info("Clean/remove files in folder: {}".format(path)) @@ -360,6 +390,7 @@ def clean(self, path: str = None): # type: ignore def report(self, filename: str = "modules.json"): "create json with list of exported modules" + wdt.feed() self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) f_name = "{}/{}".format(self.path, filename) self.log.info("Report file: {}".format(f_name)) @@ -418,12 +449,21 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] @@ -433,21 +473,29 @@ def _info(): # type:() -> dict[str, str] "version": "", "build": "", "ver": "", - "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( sys.implementation._mpy if "_mpy" in dir(sys.implementation) @@ -458,39 +506,28 @@ def _info(): # type:() -> dict[str, str] except (AttributeError, IndexError): pass gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") + read_boardname(info) gc.collect() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -512,13 +549,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -542,25 +580,94 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def read_boardname(info, desc: str = ""): + info["board"] = info["board"].replace(" ", "_") + found = False + for filename in [d + "/board_name.txt" for d in LIBS]: + wdt.feed() + # print("look up the board name in the file", filename) + if file_exists(filename): + with open(filename, "r") as file: + data = file.read() + if data: + info["board"] = data.strip() + found = True break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False + if not found: + print("Board not found, guessing board name") + descr = "" + # descr = desc or info["board"].strip() + # if "with " + info["cpu"].upper() in descr: + # # remove the with cpu part + # descr = descr.split("with " + info["cpu"].upper())[0].strip() + info["board"] = descr + + +# def read_boardname(info, desc: str = ""): +# wdt.feed() +# # print("look up the board name in the file", filename) +# if file_exists(filename): +# descr = desc or info["board"].strip() +# pos = descr.rfind(" with") +# if pos != -1: +# short_descr = descr[:pos].strip() +# else: +# short_descr = "" +# print("searching info file: {} for: '{}' or '{}'".format(filename, descr, short_descr)) +# if find_board(info, descr, filename, short_descr): +# found = True +# break +# if not found: +# print("Board not found, guessing board name") +# descr = desc or info["board"].strip() +# if "with " + info["cpu"].upper() in descr: +# # remove the with cpu part +# descr = descr.split("with " + info["cpu"].upper())[0].strip() +# info["board"] = descr +# info["board"] = info["board"].replace(" ", "_") +# gc.collect() + + +# def find_board(info: dict, descr: str, filename: str, short_descr: str): +# "Find the board in the provided board_info.csv file" +# short_hit = "" +# with open(filename, "r") as file: +# # ugly code to make testable in python and micropython +# # TODO: This is VERY slow on micropython whith MPREMOTE mount on esp32 (2-3 minutes to read file) +# while 1: +# line = file.readline() +# if not line: +# break +# descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() +# if descr_ == descr: +# info["board"] = board_ +# return True +# elif short_descr and descr_ == short_descr: +# if "with" in short_descr: +# # Good enough - no need to trawl the entire file +# info["board"] = board_ +# return True +# # good enough if not found in the rest of the file (but slow) +# short_hit = board_ +# if short_hit: +# info["board"] = short_hit +# return True +# return False def get_root() -> str: # sourcery skip: use-assigned-variable diff --git a/mip/v6/createstubs_lvgl_min.py b/mip/v6/createstubs_lvgl_min.py index eec6d93d1..cdab9a5ad 100644 --- a/mip/v6/createstubs_lvgl_min.py +++ b/mip/v6/createstubs_lvgl_min.py @@ -1,275 +1,783 @@ -t='stubber' -s='{}/{}' -r='method' -q='function' -p='bool' -o='str' -n='float' -m='int' -l='micropython' -k=Exception -j=NameError -i=sorted -h=NotImplementedError -Z=',\n' -Y='dict' -X='list' -W='tuple' -V=open -U=repr -S='_' -R=len -Q=KeyError -P=IndexError -O=dir -N=print -M=ImportError -L=True -K='family' -J='board' -I='.' -H=AttributeError -A=False -G='/' -E=None -D='version' -F=OSError -B='' -import gc as C,os,sys -from ujson import dumps as a -try:from machine import reset -except M:pass -try:from collections import OrderedDict as b -except M:from ucollections import OrderedDict as b -__version__='v1.16.0' -u=2 -v=2 -w=[I,'/lib','/sd/lib','/flash/lib','lib'] +""" +Create stubs for the lvgl modules on a MicroPython board. + +Note that the stubs can be very large, and it may be best to directly store them on an SD card if your device supports this. + +This variant was generated from createstubs.py by micropython-stubber v1.16.2 +""" +# Copyright (c) 2019-2023 Jos Verlinde + +import gc +# import logging +import os +import sys from time import sleep + +try: + from ujson import dumps +except: + from json import dumps + +try: + from machine import reset # type: ignore +except ImportError: + pass + +try: + from collections import OrderedDict +except ImportError: + from ucollections import OrderedDict # type: ignore + +try: + from nope_machine import WDT + + wdt = WDT() + +except ImportError: + + class _WDT: + def feed(self): + pass + + wdt = _WDT() + + +wdt.feed() + +__version__ = "v1.16.2" +ENOENT = 2 +_MAX_CLASS_LEVEL = 2 # Max class nesting +LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] + + class Stubber: - def __init__(A,path=E,firmware_id=E): - B=firmware_id - try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise h('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();C.collect() - if B:A._fwid=B.lower() - elif A.info[K]==l:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=C.mem_free() - if path: - if path.endswith(G):path=path[:-1] - else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:c(path+G) - except F:N('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] - def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in O(I): - if A.startswith(S)and not A in L.modules:continue - try: - E=getattr(I,A) - try:F=U(type(E)).split("'")[1] - except P:F=B - if F in{m,n,o,p,W,X,Y}:G=1 - elif F in{q,r}:G=2 - elif F in'class':G=3 - else:G=4 - D.append((A,U(E),U(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=i([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);C.collect();return D,J - def add_modules(A,modules):A.modules=i(set(A.modules)|set(modules)) - def create_all_stubs(A): - C.collect() - for B in A.modules:A.create_one_stub(B) - def create_one_stub(B,module_name): - D=module_name - if D in B.problematic:return A - if D in B.excluded:return A - H='{}/{}.py'.format(B.path,D.replace(I,G));C.collect();E=A - try:E=B.create_module_stub(D,H) - except F:return A - C.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(I,S)+'.py';H=J.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');T=C.mem_free() - except M:return A - c(H) - with V(H,'w')as N:P=str(J.info).replace('OrderedDict(',B).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(N,K,D,B) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del K - except(F,Q):pass - try:del sys.modules[D] - except Q:pass - C.collect();return L - def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';P=in_class;O=object_expr;M='Exception';H=fp;D=indent;C.collect() - if O in K.problematic:return - S,L=K.get_obj_attributes(O) - if L:N(L) - for(E,J,G,T,f)in S: - if E in['classmethod','staticmethod','BaseException',M]:continue - if E[0].isdigit():continue - if G==""and R(D)<=v*4: - U=B;V=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=M - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',P+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[r,q,'closure']): - Z=b;a=B - if P>0:a='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,Z) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,a,Z) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith("5:A[F]=B - if A[D]==B and sys.platform not in('unix','win32'): - try:i=os.uname();A[D]=i.release - except(P,H,TypeError):pass - for(j,k,m)in[(f,f,'const'),(g,g,'FAT'),(h,'pybricks.hubs','EV3Brick')]: - try:n=__import__(k,E,E,m);A[K]=j;del n;break - except(M,Q):pass - if A[K]==h:A['release']='2.0.0' - if A[K]==l: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - U=int(A[G]);X=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][U>>10] - if X:A[a]=X - A[G]='v{}.{}'.format(U&255,U>>8&3) - A[Y]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def d(info,board_descr,filename): - with V(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return L - return A -def get_root(): - try:A=os.getcwd() - except(F,H):A=I - B=A - for B in[A,'/sd','/flash',G,I]: - try:C=os.stat(B);break - except F:continue - return B -def e(filename): - try: - if os.stat(filename)[0]>>14:return L - return A - except F:return A -def f():sys.exit(1) -def read_path(): - path=B - if R(sys.argv)==3: - A=sys.argv[1].lower() - if A in('--path','-p'):path=sys.argv[2] - else:f() - elif R(sys.argv)==2:f() - return path -def g(): - try:B=bytes('abc',encoding='utf8');C=g.__module__;return A - except(h,H):return L + "Generate stubs for modules in firmware" + + def __init__(self, path: str = None, firmware_id: str = None): # type: ignore + try: + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore + raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") + except AttributeError: + pass + # self.log = logging.getLogger("stubber") + self._report = [] # type: list[str] + self.info = _info() + # self.log.info("Port: {}".format(self.info["port"])) + # self.log.info("Board: {}".format(self.info["board"])) + gc.collect() + wdt.feed() + if firmware_id: + self._fwid = firmware_id.lower() + else: + if self.info["family"] == "micropython": + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") + else: + self._fwid = "{family}-v{version}-{port}".format(**self.info) + self._start_free = gc.mem_free() # type: ignore + + if path: + if path.endswith("/"): + path = path[:-1] + else: + path = get_root() + + self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") + # self.log.debug(self.path) + try: + ensure_folder(path + "/") + except OSError: + print("error creating stub folder {}".format(path)) + self.problematic = [ + "upip", + "upysh", + "webrepl_setup", + "http_client", + "http_client_ssl", + "http_server", + "http_server_ssl", + ] + self.excluded = [ + "webrepl", + "_webrepl", + "port_diag", + "example_sub_led.py", + "example_pub_button.py", + ] + # there is no option to discover modules from micropython, list is read from an external file. + self.modules = [] # type: list[str] + + def get_obj_attributes(self, item_instance: object): + "extract information of the objects members and attributes" + # name_, repr_(value), type as text, item_instance + _result = [] + _errors = [] + # self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + for name in dir(item_instance): + if name.startswith("_") and not name in self.modules: + continue + # self.log.debug("get attribute {}".format(name)) + try: + val = getattr(item_instance, name) + # name , item_repr(value) , type as text, item_instance, order + # self.log.debug("attribute {}:{}".format(name, val)) + try: + type_text = repr(type(val)).split("'")[1] + except IndexError: + type_text = "" + if type_text in {"int", "float", "str", "bool", "tuple", "list", "dict"}: + order = 1 + elif type_text in {"function", "method"}: + order = 2 + elif type_text in ("class"): + order = 3 + else: + order = 4 + _result.append((name, repr(val), repr(type(val)), val, order)) + except AttributeError as e: + _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e)) + except MemoryError as e: + # print("MemoryError: {}".format(e)) + sleep(1) + reset() + + # remove internal __ + # _result = sorted([i for i in _result if not (i[0].startswith("_"))], key=lambda x: x[4]) + _result = sorted([i for i in _result if not (i[0].startswith("__"))], key=lambda x: x[4]) + gc.collect() + return _result, _errors + + def add_modules(self, modules): + "Add additional modules to be exported" + self.modules = sorted(set(self.modules) | set(modules)) + + def create_all_stubs(self): + "Create stubs for all configured modules" + # self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + gc.collect() + for module_name in self.modules: + self.create_one_stub(module_name) + # self.log.info("Finally done") + + def create_one_stub(self, module_name: str): + wdt.feed() + if module_name in self.problematic: + # self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + return False + if module_name in self.excluded: + # self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + return False + + file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + gc.collect() + result = False + try: + result = self.create_module_stub(module_name, file_name) + except OSError: + return False + gc.collect() + return result + + def create_module_stub(self, module_name: str, file_name: str = None) -> bool: # type: ignore + """Create a Stub of a single python module + + Args: + - module_name (str): name of the module to document. This module will be imported. + - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + """ + if file_name is None: + fname = module_name.replace(".", "_") + ".py" + file_name = self.path + "/" + fname + else: + fname = file_name.split("/")[-1] + + if "/" in module_name: + # for nested modules + module_name = module_name.replace("/", ".") + + # import the module (as new_module) to examine it + new_module = None + try: + new_module = __import__(module_name, None, None, ("*")) + m1 = gc.mem_free() # type: ignore + # self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + + except ImportError: + # self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + return False + + # Start a new file + ensure_folder(file_name) + with open(file_name, "w") as fp: + # todo: improve header + info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") + s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) + fp.write(s) + fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n") + self.write_object_stub(fp, new_module, module_name, "") + + self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + + if module_name not in {"os", "sys", "logging", "gc"}: + # try to unload the module unless we use it + try: + del new_module + except (OSError, KeyError): # lgtm [py/unreachable-statement] + pass + # lets not try - most times it does not work anyway + # try: + # del sys.modules[module_name] + # except KeyError: + pass + gc.collect() + return True + + def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0): + "Write a module/object stub to an open file. Can be called recursive." + gc.collect() + if object_expr in self.problematic: + # self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + return + + # # self.log.debug("DUMP : {}".format(object_expr)) + items, errors = self.get_obj_attributes(object_expr) + + if errors: + print(errors) + + for item_name, item_repr, item_type_txt, item_instance, _ in items: + # name_, repr_(value), type as text, item_instance, order + if item_name in ["classmethod", "staticmethod", "BaseException", "Exception"]: + # do not create stubs for these primitives + continue + if item_name[0].isdigit(): + # self.log.warning("NameError: invalid name {}".format(item_name)) + continue + # Class expansion only on first 3 levels (bit of a hack) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # self.log.info("{0}class {1}:".format(indent, item_name)) + superclass = "" + is_exception = ( + item_name.endswith("Exception") + or item_name.endswith("Error") + or item_name + in [ + "KeyboardInterrupt", + "StopIteration", + "SystemExit", + ] + ) + if is_exception: + superclass = "Exception" + s = "\n{}class {}({}):\n".format(indent, item_name, superclass) + # s += indent + " ''\n" + if is_exception: + s += indent + " ...\n" + fp.write(s) + continue + # write classdef + fp.write(s) + # first write the class literals and methods + # self.log.debug("# recursion over class {0}".format(item_name)) + self.write_object_stub( + fp, + item_instance, + "{0}.{1}".format(obj_name, item_name), + indent + " ", + in_class + 1, + ) + # end with the __init__ method to make sure that the literals are defined + # Add __init__ + s = indent + " def __init__(self, *argv, **kwargs) -> None:\n" + s += indent + " ...\n\n" + fp.write(s) + elif any(word in item_type_txt for word in ["method", "function", "closure"]): + # self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # module Function or class method + # will accept any number of params + # return type Any/Incomplete + ret = "Incomplete" + first = "" + # Self parameter only on class methods/functions + if in_class > 0: + first = "self, " + # class method - add function decoration + if "bound_method" in item_type_txt or "bound_method" in item_repr: + s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret) + else: + s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) + s += indent + " ...\n\n" + fp.write(s) + # self.log.debug("\n" + s) + elif item_type_txt == "": + # Skip imported modules + # fp.write("# import {}\n".format(item_name)) + pass + + elif item_type_txt.startswith(" dict[str, str] + info = OrderedDict( + { + "family": sys.implementation.name, + "version": "", + "build": "", + "ver": "", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", + "cpu": "", + "mpy": "", + "arch": "", + } + ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" + try: + info["version"] = version_str(sys.implementation.version) # type: ignore + except AttributeError: + pass + try: + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() + info["mpy"] = ( + sys.implementation._mpy + if "_mpy" in dir(sys.implementation) + else sys.implementation.mpy + if "mpy" in dir(sys.implementation) + else "" + ) + except (AttributeError, IndexError): + pass + gc.collect() + read_boardname(info) + gc.collect() + + try: + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): + pass + # avoid build hashes + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" + + if info["version"] == "" and sys.platform not in ("unix", "win32"): + try: + u = os.uname() # type: ignore + info["version"] = u.release + except (IndexError, AttributeError, TypeError): + pass + # detect families + for fam_name, mod_name, mod_thing in [ + ("pycopy", "pycopy", "const"), + ("pycom", "pycom", "FAT"), + ("ev3-pybricks", "pybricks.hubs", "EV3Brick"), + ]: + try: + _t = __import__(mod_name, None, None, (mod_thing)) + info["family"] = fam_name + del _t + break + except (ImportError, KeyError): + pass + + if info["family"] == "ev3-pybricks": + info["release"] = "2.0.0" + + if info["family"] == "micropython": + info["version"] + if ( + info["version"] + and info["version"].endswith(".0") + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] <= "1.19.9" + ): + # versions from 1.10.0 to 1.20.0 do not have a micro .0 + info["version"] = info["version"][:-2] + + # spell-checker: disable + if "mpy" in info and info["mpy"]: # mpy on some v1.11+ builds + sys_mpy = int(info["mpy"]) + # .mpy architecture + arch = [ + None, + "x86", + "x64", + "armv6", + "armv6m", + "armv7m", + "armv7em", + "armv7emsp", + "armv7emdp", + "xtensa", + "xtensawin", + ][sys_mpy >> 10] + if arch: + info["arch"] = arch + # .mpy version.minor + info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" + # simple to use version[-build] string + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" + + return info + + +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def read_boardname(info, desc: str = ""): + info["board"] = info["board"].replace(" ", "_") + found = False + for filename in [d + "/board_name.txt" for d in LIBS]: + wdt.feed() + # # print("look up the board name in the file", filename) + if file_exists(filename): + with open(filename, "r") as file: + data = file.read() + if data: + info["board"] = data.strip() + found = True + break + if not found: + # print("Board not found, guessing board name") + descr = "" + # descr = desc or info["board"].strip() + # if "with " + info["cpu"].upper() in descr: + # # remove the with cpu part + # descr = descr.split("with " + info["cpu"].upper())[0].strip() + info["board"] = descr + + +# def read_boardname(info, desc: str = ""): +# wdt.feed() +# # # print("look up the board name in the file", filename) +# if file_exists(filename): +# descr = desc or info["board"].strip() +# pos = descr.rfind(" with") +# if pos != -1: +# short_descr = descr[:pos].strip() +# else: +# short_descr = "" +# # print("searching info file: {} for: '{}' or '{}'".format(filename, descr, short_descr)) +# if find_board(info, descr, filename, short_descr): +# found = True +# break +# if not found: +# # print("Board not found, guessing board name") +# descr = desc or info["board"].strip() +# if "with " + info["cpu"].upper() in descr: +# # remove the with cpu part +# descr = descr.split("with " + info["cpu"].upper())[0].strip() +# info["board"] = descr +# info["board"] = info["board"].replace(" ", "_") +# gc.collect() + + +# def find_board(info: dict, descr: str, filename: str, short_descr: str): +# "Find the board in the provided board_info.csv file" +# short_hit = "" +# with open(filename, "r") as file: +# # ugly code to make testable in python and micropython +# # TODO: This is VERY slow on micropython whith MPREMOTE mount on esp32 (2-3 minutes to read file) +# while 1: +# line = file.readline() +# if not line: +# break +# descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() +# if descr_ == descr: +# info["board"] = board_ +# return True +# elif short_descr and descr_ == short_descr: +# if "with" in short_descr: +# # Good enough - no need to trawl the entire file +# info["board"] = board_ +# return True +# # good enough if not found in the rest of the file (but slow) +# short_hit = board_ +# if short_hit: +# info["board"] = short_hit +# return True +# return False + + +def get_root() -> str: # sourcery skip: use-assigned-variable + "Determine the root folder of the device" + try: + c = os.getcwd() + except (OSError, AttributeError): + # unix port + c = "." + r = c + for r in [c, "/sd", "/flash", "/", "."]: + try: + _ = os.stat(r) + break + except OSError: + continue + return r + + +def file_exists(filename: str): + try: + if os.stat(filename)[0] >> 14: + return True + return False + except OSError: + return False + + +def show_help(): + # print("-p, --path path to store the stubs in, defaults to '.'") + sys.exit(1) + + +def read_path() -> str: + "get --path from cmdline. [unix/win]" + path = "" + if len(sys.argv) == 3: + cmd = (sys.argv[1]).lower() + if cmd in ("--path", "-p"): + path = sys.argv[2] + else: + show_help() + elif len(sys.argv) == 2: + show_help() + return path + + +def is_micropython() -> bool: + "runtime test to determine full or micropython" + # pylint: disable=unused-variable,eval-used + try: + # either test should fail on micropython + # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces + # Micropython : SyntaxError + # a = eval("1and 0") # lgtm [py/unused-local-variable] + # Eval blocks some minification aspects + + # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented + # Micropython: NotImplementedError + b = bytes("abc", encoding="utf8") # type: ignore # lgtm [py/unused-local-variable] + + # c) https://docs.micropython.org/en/latest/genrst/core_language.html#function-objects-do-not-have-the-module-attribute + # Micropython: AttributeError + c = is_micropython.__module__ # type: ignore # lgtm [py/unused-local-variable] + return False + except (NotImplementedError, AttributeError): + return True + + def main(): - D='lvgl' - try:import lvgl as A - except k:return - B=D - try:B='lvgl-{0}_{1}_{2}-{3}-{4}'.format(A.version_major(),A.version_minor(),A.version_patch(),A.version_info(),sys.platform) - except k:B='lvgl-{0}_{1}_{2}_{3}-{4}'.format(8,1,0,'dev',sys.platform) - finally:stubber=Stubber(firmware_id=B) - stubber.clean();stubber.modules=['io','lodepng','rtch',D];C.collect();stubber.create_all_stubs();stubber.report() -if __name__=='__main__'or g(): - try:x=logging.getLogger(t);logging.basicConfig(level=logging.INFO) - except j:pass - if not e('no_auto_stubber.txt'): - try:C.threshold(4*1024);C.enable() - except BaseException:pass - main() \ No newline at end of file + try: + import lvgl # type: ignore + except Exception: + # print("\n\nNOTE: The `lvgl` module could not be found on this firmware\n\n") + return + # Specify firmware name & version + fw_id = "lvgl" + try: + fw_id = "lvgl-{0}_{1}_{2}-{3}-{4}".format( + lvgl.version_major(), + lvgl.version_minor(), + lvgl.version_patch(), + lvgl.version_info(), + sys.platform, + ) + except Exception: + fw_id = "lvgl-{0}_{1}_{2}_{3}-{4}".format(8, 1, 0, "dev", sys.platform) + finally: + stubber = Stubber(firmware_id=fw_id) + stubber.clean() + # modules to stub : only lvgl specifics + stubber.modules = ["io", "lodepng", "rtch", "lvgl"] # spell-checker: enable + + gc.collect() + + stubber.create_all_stubs() + stubber.report() + + +if __name__ == "__main__" or is_micropython(): + try: + log = logging.getLogger("stubber") + logging.basicConfig(level=logging.INFO) + # logging.basicConfig(level=logging.DEBUG) + except NameError: + pass + if not file_exists("no_auto_stubber.txt"): + try: + gc.threshold(4 * 1024) # type: ignore + gc.enable() + except BaseException: + pass + main() diff --git a/mip/v6/createstubs_lvgl_mpy.mpy b/mip/v6/createstubs_lvgl_mpy.mpy index 7e60951ef..ec7b3c33a 100644 Binary files a/mip/v6/createstubs_lvgl_mpy.mpy and b/mip/v6/createstubs_lvgl_mpy.mpy differ diff --git a/mip/v6/createstubs_mem.py b/mip/v6/createstubs_mem.py index edcf495ba..9a4349297 100644 --- a/mip/v6/createstubs_mem.py +++ b/mip/v6/createstubs_mem.py @@ -9,16 +9,19 @@ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device -This variant was generated from createstubs.py by micropython-stubber v1.16.0 +This variant was generated from createstubs.py by micropython-stubber v1.17.0 """ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -30,11 +33,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.0" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -42,24 +83,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -69,11 +107,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -92,20 +130,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -138,21 +179,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -167,10 +210,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -184,10 +227,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -197,21 +240,18 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") - self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True @@ -219,14 +259,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -234,11 +274,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -257,11 +302,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -275,7 +320,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -291,7 +336,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -301,36 +346,42 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -346,7 +397,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -364,41 +415,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -424,79 +487,83 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", - "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", } ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -518,13 +585,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -548,25 +616,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -619,10 +693,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -644,40 +714,40 @@ def main(): stubber.clean() # Read stubs from modulelist in the current folder or in /libs # fall back to default modules - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - _1 = gc.mem_free() # type: ignore - with open(p + "/modulelist.txt") as f: - print("Debug: List of modules: " + p + "/modulelist.txt") - line = f.readline() - while line: - line = line.strip() + def get_modulelist(stubber): + # new + gc.collect() + stubber.modules = [] # avoid duplicates + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + with open(fname) as f: + # print("DEBUG: list of modules: " + p + "/modulelist.txt") + while True: + line = f.readline().strip() + if not line: + break if len(line) > 0 and line[0] != "#": stubber.modules.append(line) - line = f.readline() - gc.collect() # type: ignore - print("Debug: Used memory to load modulelist.txt: " + str(_1 - gc.mem_free()) + " bytes") # type: ignore + gc.collect() + print("BREAK") break - except Exception: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - print("Could not find modulelist.txt, using default modules") + + if not stubber.modules: + stubber.modules = ["micropython"] + # _log.warn("Could not find modulelist.txt, using default modules") + gc.collect() + + stubber.modules = [] # avoid duplicates + get_modulelist(stubber) gc.collect() stubber.create_all_stubs() - stubber.report() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/mip/v6/createstubs_mem_min.py b/mip/v6/createstubs_mem_min.py index 03e5c7ade..9c2cfd272 100644 --- a/mip/v6/createstubs_mem_min.py +++ b/mip/v6/createstubs_mem_min.py @@ -1,281 +1,300 @@ -u='stubber' -t='{}/{}' -s='method' -r='function' -q='bool' -p='str' -o='float' -n='int' -m=NameError -l=sorted -k=NotImplementedError -b=',\n' -a='dict' -Z='list' -Y='tuple' -X='micropython' -W=str -V=repr -T='_' -S=KeyError -R=open -Q=IndexError -P=dir -O=ImportError -N=True -M='family' -L=len -K=print -J='board' -I='.' -H=AttributeError -A=False +x='No report file' +w='Failed to create the report.' +v='{}/{}' +u='method' +t='function' +s='bool' +r='str' +q='float' +p='int' +o='stubber' +n=TypeError +m=Exception +l=KeyError +k=sorted +j=NotImplementedError +e=',\n' +d='dict' +c='list' +b='tuple' +a='micropython' +Z=repr +W='-preview' +V='-' +U='board' +T=IndexError +S=print +R=True +Q='family' +P=len +O=open +N=ImportError +M=dir +K='port' +J='.' +I=AttributeError +H=False G='/' -E=None -D='version' F=OSError -C='' -import gc as B,os,sys -from ujson import dumps as c -try:from machine import reset -except O:pass -try:from collections import OrderedDict as d -except O:from ucollections import OrderedDict as d -__version__='v1.16.0' -v=2 -w=2 -e=[I,'/lib','/sd/lib','/flash/lib','lib'] +E=None +C='version' +B='' +import gc as D,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except N:pass +try:from collections import OrderedDict as f +except N:from ucollections import OrderedDict as f +__version__='v1.17.0' +y=2 +z=2 +A0=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(o) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(A,path=E,firmware_id=E): + def __init__(B,path=E,firmware_id=E): C=firmware_id try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise k('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();B.collect() - if C:A._fwid=C.lower() - elif A.info[M]==X:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=B.mem_free() + if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed') + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));D.collect() + if C:B._fwid=C.lower() + elif B.info[Q]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=D.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:f(path+G) - except F:K('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:X(path+G) + except F:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in P(I): - if A.startswith(T)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in M(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=V(type(E)).split("'")[1] - except Q:F=C - if F in{n,o,p,q,Y,Z,a}:G=1 - elif F in{r,s}:G=2 + E=getattr(H,A) + try:F=Z(type(E)).split("'")[1] + except T:F=B + if F in{p,q,r,s,b,c,d}:G=1 + elif F in{t,u}:G=2 elif F in'class':G=3 else:G=4 - D.append((A,V(E),V(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=l([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);B.collect();return D,J - def add_modules(A,modules):A.modules=l(set(A.modules)|set(modules)) - def create_all_stubs(A): - B.collect() - for C in A.modules:A.create_one_stub(C) + C.append((A,Z(E),Z(type(E)),E,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except MemoryError as J:S('MemoryError: {}'.format(J));sleep(1);reset() + C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);D.collect();return C,K + def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules)) + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();D.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') def create_one_stub(C,module_name): - D=module_name - if D in C.problematic:return A - if D in C.excluded:return A - H='{}/{}.py'.format(C.path,D.replace(I,G));B.collect();E=A - try:E=C.create_module_stub(D,H) - except F:return A - B.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:M=D.replace(I,T)+'.py';H=J.path+G+M - else:M=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');U=B.mem_free() - except O:return A - f(H) - with R(H,'w')as L:P=W(J.info).replace('OrderedDict(',C).replace('})','}');Q='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);L.write(Q);L.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(L,K,D,C) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del K - except(F,S):pass - try:del sys.modules[D] - except S:pass - B.collect();return N - def write_object_stub(M,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';Q=in_class;P=object_expr;O='Exception';H=fp;D=indent;B.collect() - if P in M.problematic:return - R,N=M.get_obj_attributes(P) - if N:K(N) - for(E,J,G,T,f)in R: - if E in['classmethod','staticmethod','BaseException',O]:continue - if E[0].isdigit():continue - if G==""and L(D)<=w*4: - U=C;V=E.endswith(O)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=O - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);M.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',Q+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[s,r,'closure']): - W=b;X=C - if Q>0:X='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,W) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,X,W) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith("5}'.format(C,L,Q)) + except N:return H + X(I) + with O(I,'w')as P:S=str(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,M,C,B) + K.report_add(C,I) + if C not in{'os','sys','logging','gc'}: + try:del M + except(F,l):A.warning('could not del new_module') + D.collect();return R + def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): + X='generator';W='{0}{1}: {3} = {2}\n';V='bound_method';U='Incomplete';N=in_class;M='Exception';L=object_expr;I=fp;E=indent;D.collect() + if L in K.problematic:A.warning('SKIPPING problematic module:{}'.format(L));return + Y,O=K.get_obj_attributes(L) + if O:A.error(O) + for(F,J,H,Z,e)in Y: + if F in['classmethod','staticmethod','BaseException',M]:continue + if F[0].isdigit():A.warning('NameError: invalid name {}'.format(F));continue + if H==""and P(E)<=z*4: + Q=B;R=F.endswith(M)or F.endswith('Error')or F in['KeyboardInterrupt','StopIteration','SystemExit'] + if R:Q=M + C='\n{}class {}({}):\n'.format(E,F,Q) + if R:C+=E+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,F),E+' ',N+1);C=E+' def __init__(self, *argv, **kwargs) -> None:\n';C+=E+' ...\n\n';I.write(C) + elif any(A in H for A in[u,t,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(E)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(E,F,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(E,F,T,S) + C+=E+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=C - if A[D]==C and sys.platform not in('unix','win32'): - try:l=os.uname();A[D]=l.release - except(Q,H,TypeError):pass - for(m,n,o)in[(i,i,'const'),(j,j,'FAT'),(k,'pybricks.hubs','EV3Brick')]: - try:p=__import__(n,E,E,o);A[M]=m;del p;break - except(O,S):pass - if A[M]==k:A['release']='2.0.0' - if A[M]==X: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - R=int(A[G]);Z=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][R>>10] - if Z:A[c]=Z - A[G]='v{}.{}'.format(R&255,R>>8&3) - A[a]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def g(info,board_descr,filename): - with R(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return N - return A + if'uname'in M(os): + A[D]=Y(os.uname()[3]) + if not A[D]:A[D]=Y(os.uname()[2]) + elif C in M(sys):A[D]=Y(sys.version) + except(I,T,n):pass + if A[C]==B and sys.platform not in(S,R): + try:b=os.uname();A[C]=b.release + except(T,I,n):pass + for(c,d,e)in[(V,V,'const'),(X,X,'FAT'),(Z,'pybricks.hubs','EV3Brick')]: + try:g=__import__(d,E,E,e);A[Q]=c;del g;break + except(N,l):pass + if A[Q]==Z:A['release']='2.0.0' + if A[Q]==a: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[P]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[D]and not A[C].endswith(W):A[C]=A[C]+W + A[L]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A +def A1(version): + A=version;B=J.join([str(A)for A in A[:3]]) + if P(A)>3 and A[3]:B+=V+A[3] + return B +def A2(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except N:A.warning('BOARDNAME not found');C=B + return C def get_root(): try:A=os.getcwd() - except(F,H):A=I + except(F,I):A=J B=A - for B in[A,'/sd','/flash',G,I]: + for B in[A,'/sd','/flash',G,J]: try:C=os.stat(B);break except F:continue return B -def h(filename): +def g(filename): try: - if os.stat(filename)[0]>>14:return N - return A - except F:return A -def i():sys.exit(1) + if os.stat(filename)[0]>>14:return R + return H + except F:return H +def h():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): - path=C - if L(sys.argv)==3: + path=B + if P(sys.argv)==3: A=sys.argv[1].lower() if A in('--path','-p'):path=sys.argv[2] - else:i() - elif L(sys.argv)==2:i() + else:h() + elif P(sys.argv)==2:h() return path -def j(): - try:B=bytes('abc',encoding='utf8');C=j.__module__;return A - except(k,H):return N +def i(): + try:A=bytes('abc',encoding='utf8');B=i.__module__;return H + except(j,I):return R def main(): - E='/modulelist.txt';stubber=Stubber(path=read_path());stubber.clean();stubber.modules=[] - for C in e: - try: - F=B.mem_free() - with R(C+E)as D: - K('Debug: List of modules: '+C+E);A=D.readline() - while A: - A=A.strip() - if L(A)>0 and A[0]!='#':stubber.modules.append(A) - A=D.readline() - B.collect();K('Debug: Used memory to load modulelist.txt: '+W(F-B.mem_free())+' bytes');break - except Exception:pass - if not stubber.modules:stubber.modules=[X] - B.collect();stubber.create_all_stubs();stubber.report() -if __name__=='__main__'or j(): - try:x=logging.getLogger(u);logging.basicConfig(level=logging.INFO) - except m:pass - if not h('no_auto_stubber.txt'): - try:B.threshold(4*1024);B.enable() + stubber=Stubber(path=read_path());stubber.clean() + def A(stubber): + D.collect();stubber.modules=[] + for C in A0: + B=C+'/modulelist.txt' + if not g(B):continue + with O(B)as E: + while R: + A=E.readline().strip() + if not A:break + if P(A)>0 and A[0]!='#':stubber.modules.append(A) + D.collect();S('BREAK');break + if not stubber.modules:stubber.modules=[a] + D.collect() + stubber.modules=[];A(stubber);D.collect();stubber.create_all_stubs() +if __name__=='__main__'or i(): + if not g('no_auto_stubber.txt'): + try:D.threshold(4*1024);D.enable() except BaseException:pass main() \ No newline at end of file diff --git a/mip/v6/createstubs_mem_mpy.mpy b/mip/v6/createstubs_mem_mpy.mpy index 7720c2497..958459543 100644 Binary files a/mip/v6/createstubs_mem_mpy.mpy and b/mip/v6/createstubs_mem_mpy.mpy differ diff --git a/mip/v6/createstubs_min.py b/mip/v6/createstubs_min.py index db0387e80..63ab05685 100644 --- a/mip/v6/createstubs_min.py +++ b/mip/v6/createstubs_min.py @@ -1,254 +1,274 @@ -w='pyb' -v='stubber' -u='{}/{}' -t='logging' -s='sys' -r='method' -q='function' -p='bool' -o='str' -n='float' -m='int' -l=NameError +z='No report file' +y='Failed to create the report.' +x='{}/{}' +w='logging' +v='sys' +u='method' +t='function' +s='bool' +r='str' +q='float' +p='int' +o='stubber' +n=TypeError +m=Exception +l=KeyError k=sorted j=NotImplementedError -b='pycom' -a=',\n' -Z='dict' -Y='list' -X='tuple' -W='micropython' -V=open -U=repr -S='_' +f='pycom' +e=',\n' +d='dict' +c='list' +b='tuple' +a='micropython' +Z=repr +Y=print +V='-preview' +U=True +T='-' +S='board' R=len -Q=KeyError +Q=open P=IndexError -O=dir -N=print -M=ImportError -L=True -K='family' -J='board' -I='.' -H=AttributeError -A=False +O='family' +N=ImportError +M=dir +K='port' +J='.' +I=AttributeError +H=False G='/' -E=None -F=OSError -D='version' +E=OSError +D=None +C='version' B='' -import gc as C,os,sys -from ujson import dumps as c -try:from machine import reset -except M:pass -try:from collections import OrderedDict as d -except M:from ucollections import OrderedDict as d -__version__='v1.16.0' -x=2 -y=2 -z=[I,'/lib','/sd/lib','/flash/lib','lib'] +import gc as F,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except N:pass +try:from collections import OrderedDict as g +except N:from ucollections import OrderedDict as g +__version__='v1.17.0' +A0=2 +A1=2 +A5=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=Y + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(o) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(A,path=E,firmware_id=E): - B=firmware_id + def __init__(B,path=D,firmware_id=D): + C=firmware_id try: if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();C.collect() - if B:A._fwid=B.lower() - elif A.info[K]==W:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=C.mem_free() + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[S]));F.collect() + if C:B._fwid=C.lower() + elif B.info[O]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(T) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=F.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:e(path+G) - except F:N('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:W(path+G) + except E:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in O(I): - if A.startswith(S)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in M(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=U(type(E)).split("'")[1] - except P:F=B - if F in{m,n,o,p,X,Y,Z}:G=1 - elif F in{q,r}:G=2 - elif F in'class':G=3 + D=getattr(H,A) + try:E=Z(type(D)).split("'")[1] + except P:E=B + if E in{p,q,r,s,b,c,d}:G=1 + elif E in{t,u}:G=2 + elif E in'class':G=3 else:G=4 - D.append((A,U(E),U(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=k([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);C.collect();return D,J + C.append((A,Z(D),Z(type(D)),D,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except MemoryError as J:Y('MemoryError: {}'.format(J));sleep(1);reset() + C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules)) - def create_all_stubs(A): - C.collect() - for B in A.modules:A.create_one_stub(B) - def create_one_stub(B,module_name): - D=module_name - if D in B.problematic:return A - if D in B.excluded:return A - H='{}/{}.py'.format(B.path,D.replace(I,G));C.collect();E=A - try:E=B.create_module_stub(D,H) - except F:return A - C.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(I,S)+'.py';H=J.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');T=C.mem_free() - except M:return A - e(H) - with V(H,'w')as N:P=str(J.info).replace('OrderedDict(',B).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(N,K,D,B) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os',s,t,'gc'}: - try:del K - except(F,Q):pass - try:del sys.modules[D] - except Q:pass - C.collect();return L + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') + def create_one_stub(C,module_name): + B=module_name + if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H + if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H + I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();D=H + try:D=C.create_module_stub(B,I) + except E:return H + F.collect();return D + def create_module_stub(K,module_name,file_name=D): + I=file_name;C=module_name + if I is D:L=C.replace(J,'_')+'.pyi';I=K.path+G+L + else:L=I.split(G)[-1] + if G in C:C=C.replace(G,J) + M=D + try:M=__import__(C,D,D,'*');P=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,P)) + except N:return H + W(I) + 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) + K.report_add(C,I) + if C not in{'os',v,w,'gc'}: + try:del M + except(E,l):A.warning('could not del new_module') + F.collect();return U def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';P=in_class;O=object_expr;M='Exception';H=fp;D=indent;C.collect() - if O in K.problematic:return - S,L=K.get_obj_attributes(O) - if L:N(L) - for(E,J,G,T,f)in S: + X='generator';W='{0}{1}: {3} = {2}\n';V='bound_method';U='Incomplete';N=in_class;M='Exception';L=object_expr;I=fp;D=indent;F.collect() + if L in K.problematic:A.warning('SKIPPING problematic module:{}'.format(L));return + Y,O=K.get_obj_attributes(L) + if O:A.error(O) + for(E,J,H,Z,e)in Y: if E in['classmethod','staticmethod','BaseException',M]:continue - if E[0].isdigit():continue - if G==""and R(D)<=y*4: - U=B;V=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=M - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',P+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[r,q,'closure']): - W=b;a=B - if P>0:a='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,W) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,a,W) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith(""and R(D)<=A1*4: + P=B;Q=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] + if Q:P=M + C='\n{}class {}({}):\n'.format(D,E,P) + if Q:C+=D+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C) + elif any(A in H for A in[u,t,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S) + C+=D+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=B - if A[D]==B and sys.platform not in('unix','win32'): - try:j=os.uname();A[D]=j.release - except(P,H,TypeError):pass - for(k,l,m)in[(h,h,'const'),(b,b,'FAT'),(i,'pybricks.hubs','EV3Brick')]: - try:n=__import__(l,E,E,m);A[K]=k;del n;break - except(M,Q):pass - if A[K]==i:A['release']='2.0.0' - if A[K]==W: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if G in A and A[G]: - U=int(A[G]);Y=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][U>>10] - if Y:A[c]=Y - A[G]='v{}.{}'.format(U&255,U>>8&3) - A[Z]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def f(info,board_descr,filename): - with V(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return L - return A + if'uname'in M(os): + A[E]=X(os.uname()[3]) + if not A[E]:A[E]=X(os.uname()[2]) + elif C in M(sys):A[E]=X(sys.version) + except(I,P,n):pass + if A[C]==B and sys.platform not in(U,T): + try:Z=os.uname();A[C]=Z.release + except(P,I,n):pass + for(b,c,d)in[(W,W,'const'),(f,f,'FAT'),(Y,'pybricks.hubs','EV3Brick')]: + try:e=__import__(c,D,D,d);A[O]=b;del e;break + except(N,l):pass + if A[O]==Y:A['release']='2.0.0' + if A[O]==a: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[D,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[R]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[E]and not A[C].endswith(V):A[C]=A[C]+V + A[L]=f"{A[C]}-{A[E]}"if A[E]else f"{A[C]}";return A +def A2(version): + A=version;B=J.join([str(A)for A in A[:3]]) + if R(A)>3 and A[3]:B+=T+A[3] + return B +def A3(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except N:A.warning('BOARDNAME not found');C=B + return C def get_root(): try:A=os.getcwd() - except(F,H):A=I + except(E,I):A=J B=A - for B in[A,'/sd','/flash',G,I]: + for B in[A,'/sd','/flash',G,J]: try:C=os.stat(B);break - except F:continue + except E:continue return B -def g(filename): +def A4(filename): try: - if os.stat(filename)[0]>>14:return L - return A - except F:return A -def h():sys.exit(1) + if os.stat(filename)[0]>>14:return U + return H + except E:return H +def h():Y("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): path=B if R(sys.argv)==3: @@ -258,13 +278,11 @@ def read_path(): elif R(sys.argv)==2:h() return path def i(): - try:B=bytes('abc',encoding='utf8');C=i.__module__;return A - except(j,H):return L -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','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','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',t,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',W,'mip','mip/__init__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform',w,b,'pye','qrcode','queue','random','requests','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',s,'time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',D,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];C.collect();stubber.create_all_stubs();stubber.report() + try:A=bytes('abc',encoding='utf8');B=i.__module__;return H + except(j,I):return U +def main():stubber=Stubber(path=read_path());stubber.clean();stubber.modules=['WM8960','_OTA','_asyncio','_boot_fat','_coap','_espnow','_flash_control_OTA','_main_pybytes','_mqtt','_mqtt_core','_msg_handl','_onewire','_periodical_pin','_pybytes','_pybytes_ca','_pybytes_config','_pybytes_config_reader','_pybytes_connection','_pybytes_constants','_pybytes_debug','_pybytes_library','_pybytes_machine_learning','_pybytes_main','_pybytes_protocol','_pybytes_pyconfig','_pybytes_pymesh_config','_rp2','_terminal','_thread','_uasyncio','_urequest','adcfft','aioble/__init__','aioble/central','aioble/client','aioble/core','aioble/device','aioble/l2cap','aioble/peripheral','aioble/security','aioble/server','aioespnow','ak8963','apa102','apa106','argparse','array','asyncio/__init__','asyncio/core','asyncio/event','asyncio/funcs','asyncio/lock','asyncio/stream','binascii','bluetooth','breakout_as7262','breakout_bh1745','breakout_bme280','breakout_bme68x','breakout_bmp280','breakout_dotmatrix','breakout_encoder','breakout_icp10125','breakout_ioexpander','breakout_ltr559','breakout_matrix11x7','breakout_mics6814','breakout_msa301','breakout_paa5100','breakout_pmw3901','breakout_potentiometer','breakout_rgbmatrix5x5','breakout_rtc','breakout_scd41','breakout_sgp30','breakout_trackball','breakout_vl53l5cx','btree','cmath','collections','crypto','cryptolib','curl','deflate','dht','display','display_driver_utils','ds18x20','encoder','errno','esp','esp32','espidf','espnow','ffi','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',w,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',a,'mip','mip/__init__','mip/__main__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform','pyb',f,'pye','qrcode','queue','random','requests','requests/__init__','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',v,'termios','time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',C,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];F.collect();stubber.create_all_stubs() if __name__=='__main__'or i(): - try:A0=logging.getLogger(v);logging.basicConfig(level=logging.INFO) - except l:pass - if not g('no_auto_stubber.txt'): - try:C.threshold(4*1024);C.enable() + if not A4('no_auto_stubber.txt'): + try:F.threshold(4*1024);F.enable() except BaseException:pass main() \ No newline at end of file diff --git a/mip/v6/createstubs_mpy.mpy b/mip/v6/createstubs_mpy.mpy index f1ada4478..79c022b32 100644 Binary files a/mip/v6/createstubs_mpy.mpy and b/mip/v6/createstubs_mpy.mpy differ diff --git a/mip/v6/modulelist.txt b/mip/v6/modulelist.txt index 16a92863c..32dacba0f 100644 --- a/mip/v6/modulelist.txt +++ b/mip/v6/modulelist.txt @@ -1,5 +1,4 @@ -# list of modules to stub. -# used by the memory optimised createstubs_mem.py +# list of modules to stub used by the memory optimised createstubs_mem.py WM8960 _OTA _asyncio @@ -45,6 +44,7 @@ aioespnow ak8963 apa102 apa106 +argparse array asyncio/__init__ asyncio/core @@ -93,6 +93,7 @@ esp esp32 espidf espnow +ffi flashbdev framebuf freesans20 @@ -129,6 +130,7 @@ microWebTemplate micropython mip mip/__init__ +mip/__main__ motor mpu6500 mpu9250 @@ -156,6 +158,7 @@ qrcode queue random requests +requests/__init__ rp2 rtch samd @@ -168,6 +171,7 @@ ssl stm struct sys +termios time tpcalib uarray diff --git a/package.json b/package.json index c4ff276b6..0559c3106 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,22 @@ { - "urls": [ - [ - "createstubs.py", - "github:josverl/micropython-stubber/src/stubber/board/createstubs_min.py" - ], - [ - "createstubs_db.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db_min.py" - ], - [ - "createstubs_mem.py", - "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem_min.py" - ], - [ - "modules.txt", - "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" - ] + "urls": [ + [ + "createstubs.py", + "github:josverl/micropython-stubber/src/stubber/board/createstubs_min.py" ], - "deps": [], - "version": "1.16.2" -} \ No newline at end of file + [ + "createstubs_db.py", + "github:Josverl/micropython-stubber/src/stubber/board/createstubs_db_min.py" + ], + [ + "createstubs_mem.py", + "github:Josverl/micropython-stubber/src/stubber/board/createstubs_mem_min.py" + ], + [ + "modulelist.txt", + "github:Josverl/micropython-stubber/src/stubber/board/modulelist.txt" + ] + ], + "deps": [], + "version": "1.17.0" +} diff --git a/poetry.lock b/poetry.lock index 6180247fd..7c2d57256 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,14 +1,14 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" +version = "0.7.16" +description = "A light, configurable Sphinx theme" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] @@ -46,27 +46,38 @@ files = [ [[package]] name = "argcomplete" -version = "3.2.1" +version = "3.2.2" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" files = [ - {file = "argcomplete-3.2.1-py3-none-any.whl", hash = "sha256:30891d87f3c1abe091f2142613c9d33cac84a5e15404489f033b20399b691fec"}, - {file = "argcomplete-3.2.1.tar.gz", hash = "sha256:437f67fb9b058da5a090df505ef9be0297c4883993f3f56cb186ff087778cfb4"}, + {file = "argcomplete-3.2.2-py3-none-any.whl", hash = "sha256:e44f4e7985883ab3e73a103ef0acd27299dbfe2dfed00142c35d4ddd3005901d"}, + {file = "argcomplete-3.2.2.tar.gz", hash = "sha256:f3e49e8ea59b4026ee29548e24488af46e30c9de57d48638e24f54a1ea1000a2"}, ] [package.extras] test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] +[[package]] +name = "argparse-addons" +version = "0.12.0" +description = "Additional argparse types and actions." +optional = false +python-versions = ">=3.6" +files = [ + {file = "argparse_addons-0.12.0-py3-none-any.whl", hash = "sha256:48b70ecd719054fcb0d7e6f25a1fecc13607aac61d446e83f47d211b4ead0d61"}, + {file = "argparse_addons-0.12.0.tar.gz", hash = "sha256:6322a0dcd706887e76308d23136d5b86da0eab75a282dc6496701d1210b460af"}, +] + [[package]] name = "astroid" -version = "3.0.2" +version = "3.0.3" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.8.0" files = [ - {file = "astroid-3.0.2-py3-none-any.whl", hash = "sha256:d6e62862355f60e716164082d6b4b041d38e2a8cf1c7cd953ded5108bac8ff5c"}, - {file = "astroid-3.0.2.tar.gz", hash = "sha256:4a61cf0a59097c7bb52689b0fd63717cd2a8a14dc9f1eee97b82d814881c8c91"}, + {file = "astroid-3.0.3-py3-none-any.whl", hash = "sha256:92fcf218b89f449cdf9f7b39a269f8d5d617b27be68434912e11e79203963a17"}, + {file = "astroid-3.0.3.tar.gz", hash = "sha256:4148645659b08b70d72460ed1921158027a9e53ae8b7234149b1400eddacbb93"}, ] [package.dependencies] @@ -92,21 +103,22 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "autoflake" @@ -139,151 +151,170 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" -version = "4.12.2" +version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "bincopy" +version = "20.0.0" +description = "Mangling of various file formats that conveys binary information (Motorola S-Record, Intel HEX and binary files)." +optional = false +python-versions = ">=3.6" +files = [ + {file = "bincopy-20.0.0-py3-none-any.whl", hash = "sha256:c9cde5234e3699aa9eaaab4a4e7f32f175d2111549ae4423ee19012840714426"}, + {file = "bincopy-20.0.0.tar.gz", hash = "sha256:14cfb4cf97227bf2b1f5b85623df4c767ad219afdc9fe0732dd2cfdff446afdf"}, +] + +[package.dependencies] +argparse-addons = ">=0.4.0" +humanfriendly = "*" +pyelftools = "*" + [[package]] name = "bitarray" -version = "2.9.1" +version = "2.9.2" description = "efficient arrays of booleans -- C extension" optional = true python-versions = "*" files = [ - {file = "bitarray-2.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8ec486fee4b4c85e857233894147db04aa8a80729ccfaf92f24b25c2b6a62ece"}, - {file = "bitarray-2.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80b74b8161d70d328daad0da26b5af31491414872d2c6418bba1211de74d9c6a"}, - {file = "bitarray-2.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:afa634f34d3a843ced2b1ffbec926162376e1d003de87265eab8ded80f17cbb6"}, - {file = "bitarray-2.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d54b67a0e130909663e0cd750d50e0ad82375b639d6b8ef18a724f21807baa5"}, - {file = "bitarray-2.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d8c812bc06b178a0be78aa92d70629e29e23c0ece8d4fc925f7e06d5499e3c8"}, - {file = "bitarray-2.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:47cfa2afdff14e4fa89e528e41f9bea42345006c1b1c029021f5f93715fa6e5a"}, - {file = "bitarray-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68e2b8143a9cf4e2a9bbcb427ce064cdefa07da41a9e28898539acf1c2702e5c"}, - {file = "bitarray-2.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872ddaec39826bbf1f3214b86644ff021f8fee116ba3c428d979818e81b309ca"}, - {file = "bitarray-2.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:354f52be0270bb2e113f0263c931f87e9e1e9d22c3c80c77595ea09303f315eb"}, - {file = "bitarray-2.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31701863882c012abf94d5e2e9e6e2d5084b385cead973e87949f9ff4f253d32"}, - {file = "bitarray-2.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2f46cab76a1c9f86820ee3b6be0c04b0f0d8002461abf4a6e6453e7686e559a3"}, - {file = "bitarray-2.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f335516aaf14192b1af652bd0f76ab5b592b59622ae274b99f7638ca2f2da797"}, - {file = "bitarray-2.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:539f401530fb97d2dff5fefe7f022f543fe0ecc0d107bdd2a44e1173e55d3c35"}, - {file = "bitarray-2.9.1-cp310-cp310-win32.whl", hash = "sha256:aef8bd602108c451be736f741ae1dcf535c271ed6a43ae927f03ed3d34154b73"}, - {file = "bitarray-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:268dc7ba83277560c3fc93cb31fb79729a93e7e266db2a083bd7475db52c220b"}, - {file = "bitarray-2.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7443154f32dd9da2848cc95f7006fb9f9c191b8fa23908aa0dcf9b41e40e8913"}, - {file = "bitarray-2.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:137d94a7f1671e74f8cf9bda3b3c6734f4d6282b223a3abb6dd2b285dd7ccef7"}, - {file = "bitarray-2.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:17a504917f5f24decbc865fa21068cac9e8a1fcba32e8d09d4e29d8f6fec6804"}, - {file = "bitarray-2.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a385754699017f71c24c210b7a0854f32e38ba02c054cd5b0e26b40fb7cc0f"}, - {file = "bitarray-2.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a462e6d58fcb6554a99a143f9cd5971111a4122fa650899c98c67738581b3314"}, - {file = "bitarray-2.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51c2be97985aad8b99598bfa80bd9d5ea1551a0fdfbee64a1d5bc345246cab3c"}, - {file = "bitarray-2.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be22aa7738e523d2cd26f2a1ddec3b061d2bf5274e1f8ad75dae9622bbb09b2f"}, - {file = "bitarray-2.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ab965ad3f69abb31acf10b12596fbff95b8ef38351eee987fc60154f8ca087"}, - {file = "bitarray-2.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8b0743cf136c10f7116b2a55470ebc9b947ec05870a8a014e70f38ecfa33644"}, - {file = "bitarray-2.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b4954df8a45f231098e37ce3f58954a3da383e8d25262efab80a6b1249bff910"}, - {file = "bitarray-2.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1dc49fc767e202f3c2218214c9812e251c3d8a04fba8a288b44280e2a89d7f71"}, - {file = "bitarray-2.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:22fe50ac048895d7b5afe6f00f430146e82d89726ff594513237f717b394364a"}, - {file = "bitarray-2.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cae20700e81031d24681cfdf237812b51cb682a0fe62b3200e8f24b7c2e8d784"}, - {file = "bitarray-2.9.1-cp311-cp311-win32.whl", hash = "sha256:f6861d897253cafb19ca539debd94ac38e863cdeeae32467520d1ded9f916262"}, - {file = "bitarray-2.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6ea4863c425c4c1ea39cb481b3d76a4cf4abd6835bc71d54c2ce67e693397ca"}, - {file = "bitarray-2.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:15e218ad7650e362818172035ac3f6bc473d3a62a776b5126c28434af42d08f2"}, - {file = "bitarray-2.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:983f83346b01b62862bcfc63ef7feddd650b8a1f149a09196aa9585ef6821fce"}, - {file = "bitarray-2.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a3fb39868d9f4725a578b2fc28af8416bc457e1e40d89534943d91cc1beb045"}, - {file = "bitarray-2.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b28926b78891ef648387e788d6efabbc5ae4a54fd90cc03414c2997dc9e7e39"}, - {file = "bitarray-2.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab456f15b3e472c157080be6d93736021048efdc1c564cf156c995390f9a6df1"}, - {file = "bitarray-2.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f006488b99ebe6abeb1e8527808fb52535413c01b321d3d2d651b4526e4b87d"}, - {file = "bitarray-2.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2b3dc3d70102ef7096cc793f998ff6de18ae5d31bf1acfd5a776d392dcc95"}, - {file = "bitarray-2.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f21ed516368d8ed985a72bd6451a09f5e8835a238a217705fba301b68aa1a4ef"}, - {file = "bitarray-2.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:81dd94c7f48674f98c9e05a1cddf64a8803ece5aed142889a82a26fda3c71794"}, - {file = "bitarray-2.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:378666bb81da6a5fe3bba43771f53a41dfb898fef3c1e900ea5db165b28f46f3"}, - {file = "bitarray-2.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b2eed5ddd7a527bd08a48427cecd91b51ded0cfa5cb00ee09dcb5055d8f07d4a"}, - {file = "bitarray-2.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:6b044b336f9919dbd2aa77c7c3cebd16e03aae58e7360f2793ab2ea0bc524fd3"}, - {file = "bitarray-2.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:04ecddb02408b2875cca7ac1b77d4947bfea1cd975bb5ed48dc03ce3dcb71b88"}, - {file = "bitarray-2.9.1-cp312-cp312-win32.whl", hash = "sha256:831179eddbc7f536d9644ad1c6b90174f160489aab21c81306e6dc0fb4d89b55"}, - {file = "bitarray-2.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fd507e9b20528bfbf414ab0a78ee3670ec546753efc6340ad52afcc2c8262d3"}, - {file = "bitarray-2.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:794d083a0fbb88aed2c76ff2dc2cd6d48807a02a51c22f078ce2de9305680b93"}, - {file = "bitarray-2.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0360a341901e7c260cbce8029ee11d0e5a69f45b5da41f34bb94bcc8e50bfcc"}, - {file = "bitarray-2.9.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ee15d96cc60f0d430e3e08ff859ae2ee82f66bf2a5ed6d8d4daf3c00386e64b"}, - {file = "bitarray-2.9.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115d3fd159ed3f00e6630d54d1158fe641063d5150d689d071266cc352ce7767"}, - {file = "bitarray-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d2a0ec86d79894198fe3eeba2f9d679dbd2ae8c36a06fcbb00af1fa05e2d9b4"}, - {file = "bitarray-2.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f78325b67be856092b7d4e517d082a9470161965887d6319a185312b24fda290"}, - {file = "bitarray-2.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:274e378532959d5c32024a6858540f5e65c7f3c4956ed2165642ecf018f49f8b"}, - {file = "bitarray-2.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:03307cd4e6f280aa6250fc0324cec6a5b82089c8ba42afedff2288a97aea2e4f"}, - {file = "bitarray-2.9.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:f0493296b1d68eabd96e90faacd3524075f44b4c65a58533c9fca24663dfc70b"}, - {file = "bitarray-2.9.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:058b6bcd3fe33782195faed175bf95924f501c12f60eff5945c9cce47ba8166c"}, - {file = "bitarray-2.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:742cae481cdc29754091edf60c192986e36d15fc096d5970632286897e5bc704"}, - {file = "bitarray-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:edfb1d32a317816803628c1567e0adb8cd1a10aadfbe468b5dd169acec976d0f"}, - {file = "bitarray-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:427aa32053b9bb5e9100a35331354f5e242928d8b3d95cba80e52616f9df2dae"}, - {file = "bitarray-2.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:635fb091b1b0ac907248bb04e89eb4cce99c6a3b311a9471047e91da8a32ae03"}, - {file = "bitarray-2.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fb4ba4ac0259237c2c7cd09f40370961edf7f44916fe68d8c6ddececbd8e657"}, - {file = "bitarray-2.9.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d722cb746f3497afab33be20dc2290e1b8b423c0fb98249562391c8e0d80983f"}, - {file = "bitarray-2.9.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53c566f16ee61f077bed6fe527f4d98f218815afce3bd3d04d1c96309046fb66"}, - {file = "bitarray-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3a54c043b8c5118afc9d5908dde47fc7120b95dcc35dc1ff2dae98e04a016d"}, - {file = "bitarray-2.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ceab4174c04b72c50415b2e498361cd975fd48bf86eca420d997e4ff843da428"}, - {file = "bitarray-2.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4484695f3a21feda1cd940be5094568e71f00336e6517034a2f144b40e9dcfd6"}, - {file = "bitarray-2.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f89f501fec6796a03b6edce69e98f26915fd8699f3f810b137d680405ec8f803"}, - {file = "bitarray-2.9.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:89ef30a3d1c77a1cc7a3ee3b9a8e62c3fa5087b5b6d33076ca94fe0c9016cb33"}, - {file = "bitarray-2.9.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:a00273a9fbf9acad1dcc2a95fbe39aa3c155c5928faa2e1a64a6902aa5bde8de"}, - {file = "bitarray-2.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7c12a18f92078d2e010039f4df06d05619e347030f3eeb42291e75d9a85b7a5a"}, - {file = "bitarray-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:7fa1ad546828934cf66cb96faf3aba91cabac912fcfb225c29192bb1ee22d897"}, - {file = "bitarray-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a884ae1b13a592528db650cd44477bab3c7baa010dca58e751bd9c6a9544ab00"}, - {file = "bitarray-2.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6abe98c480a9d3306fd3abe615000e08f8d83dab77e5ac64252d58e9c63425b6"}, - {file = "bitarray-2.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a7e032cb15248c8c8648c0d3634e5637fcdf3862cbb8e423d0384c24ff049f85"}, - {file = "bitarray-2.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2698defa82f19031541cb106b64eedd6d99fbe14357becad9bec9fef20d7d94"}, - {file = "bitarray-2.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09ec8ac02819ee45a2d34c3a831821be507707446bb877844cacf848a082891e"}, - {file = "bitarray-2.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:923c730f2129acd2a5a885d9652ec040e1f13296d7cfe98121c960f4a34995ba"}, - {file = "bitarray-2.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35d5ff4330d54c761c0d685cdfb278a97b122fcdb33bf416f7725da45e44bff8"}, - {file = "bitarray-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f1d3778f492c4c04ecf2f737907ff8fb5b486dbe9eff7ea2e482cb3409be763"}, - {file = "bitarray-2.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdc1c1fa4a41388b227dc69b5e774523e3fda8b78dedb147239921e41cf42792"}, - {file = "bitarray-2.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b198869d951ad5364738ad96899f8f3dbe69e66a8096c67daad51ec64cac9e53"}, - {file = "bitarray-2.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1245317dba8cf67e67c8b03bd7efdfa0d417dcdf3bbb36a2a0e2b5eb33598724"}, - {file = "bitarray-2.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f8257428698c00b4f940729d19f8d625397da0ad3d2858781635e991bc6a8fdd"}, - {file = "bitarray-2.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:492a4b2fedeb9aaf03b49c07488296328ab6c77aeb28e4cc6c8770a7a4542ede"}, - {file = "bitarray-2.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f132423dc44fd6952f7e57043590e7a39242ae9be8bbbb5071fbd1d09d5e729f"}, - {file = "bitarray-2.9.1-cp38-cp38-win32.whl", hash = "sha256:9854301bfa2e0601f9811763f9fc49ede9fd5065d5ff19e528a5d795f38d8ef5"}, - {file = "bitarray-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:c4dcd9658c88b766e1195e0aad96bc63ad12c4d075435c3e62f6d28099f6e0ba"}, - {file = "bitarray-2.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f57ee5356581d9fa39f356dec9e4986cee686953f90a202bf68eb307ba1c2e4"}, - {file = "bitarray-2.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14ccc21b9c6f6e2bc5c3321cbc684f801a29c09b6f07a3b6c7ba2aa5a44d9600"}, - {file = "bitarray-2.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:32276c8e591d11622bd32ac4961938b80786f367b1d0e4c06edb9352b1aafec8"}, - {file = "bitarray-2.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e1d553ee8d030f18bfcf5c23db8e590fd7a5ad6d31ef056fb1dd427a499076"}, - {file = "bitarray-2.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:639c7da5dc2621cfb74b7c8a5dc6b459b8de96877d72df0017a0497365d6a3a4"}, - {file = "bitarray-2.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da310f1097c1ddafbef807a7450b86246c8c78583777845ee31fbdaf552b3937"}, - {file = "bitarray-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08d2afd6afccd0aeea44ebc37ebca9c47da3807cd54b59b31d7c4a71a2e47eb"}, - {file = "bitarray-2.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e48a13405c042594f0f58c3687c88ec2392ea040fd878af17fdb7eea1f03da5a"}, - {file = "bitarray-2.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:af8f0dcc6c2f79e8c0267752435061a05b0b979a02be5a963a4f83a396f0d565"}, - {file = "bitarray-2.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:63e600876575af3f39cdb2cd8fd5d12d14fa388bd2e2750a0ac7e0f0aa86aed9"}, - {file = "bitarray-2.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8ed54165b0f2a12b3b1dec8a2fee8b3861bc3547c4b3619e665a22f00a738d"}, - {file = "bitarray-2.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0ff68b4b25530b83b8267644f18326a601bc6a7ce229551172df02bec4119bc7"}, - {file = "bitarray-2.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f6bab4b4756c0f625ebab3ae3403b593b11e5e28666d0fb94db66dd76b91a70"}, - {file = "bitarray-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b775eab6fd07e409e64342e701c36d209f0bdb41d58c129f3541f254d35ad7e"}, - {file = "bitarray-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:3d9748736f76e5abefad9cc12ea3ac963e66a69594d3eeb9dcad13f82144c855"}, - {file = "bitarray-2.9.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cbba0d90fefe13d88fa134973f9af8a6a45b22f75f1ad9170720c446bb785b6b"}, - {file = "bitarray-2.9.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad08f46d407bb4b58ad4f805989bb54b942ba47d1e1e44750d0d02d857a4946"}, - {file = "bitarray-2.9.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b4835e82743f6be45b4f753c165055f1c16a3a77c4cd8751e685aaa16373d5"}, - {file = "bitarray-2.9.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39dbb10646c40374acf01087e9d8e2bf4f3f3496f5d78bac3cf433d8de067110"}, - {file = "bitarray-2.9.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8acac7d7899ea482aca7a64575eae4128c05eae65b27a6d175478d9cceaf170c"}, - {file = "bitarray-2.9.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c125065eaef5bf542e235f49389f3c3a519b198bb38e100b89b1dbd4fdd47215"}, - {file = "bitarray-2.9.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a853c25643de3484b7d3aba55b6fcbbe45914f2063268793618c14c4656252d3"}, - {file = "bitarray-2.9.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da5d231315999808ba90b054e0f68f9f915119a0320dc072b3bcfc46e2ac04cb"}, - {file = "bitarray-2.9.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ece8ce9f4f8f6ebb21afcfa9f63b6d13b20d6415e1df6ac347bfc6a60c429b73"}, - {file = "bitarray-2.9.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9cc822bc3dd3ae0517ea2a2f1581ceeb087b9812f49bd6c2142889af8e8c2bee"}, - {file = "bitarray-2.9.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7314411d0802c5a22f48c80a4af1acb43a81eb7feb8b666b56dce6fe345c53f"}, - {file = "bitarray-2.9.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbd17bcbcee4a827ebab4e420fadae75cf942a668020f0721ab9a4b4e265c32c"}, - {file = "bitarray-2.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6c1c089e2d8dbaad00b3ffdad7063f85e578c0d9a88db72be8ced8a8885e38a"}, - {file = "bitarray-2.9.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52311769fb6f1c22266b6da4c4f673022d5421551fadae6c98dd51fcd4b14201"}, - {file = "bitarray-2.9.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2303b71e1dc93f62b9477f54cf52b00cabe41c557dc9a59bd650e66e3eaf489b"}, - {file = "bitarray-2.9.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9e8d46ed78c50362cfe34f15c4dd640331ee6b714fb38c3022a2a6e7f8e38dbf"}, - {file = "bitarray-2.9.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93b41a09171d136acdc36c1b45bfb67dbe3e54193622462a6a3a29e5e18356fc"}, - {file = "bitarray-2.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:289645fc9abe39a726e1a213bd20303a39c80daeaa3617bce0459181f029e281"}, - {file = "bitarray-2.9.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0378955409e1c0645ac1fa50bdf5d3cbd2952e0953ed96270900e6832354afc"}, - {file = "bitarray-2.9.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:43e7b8297bc2928562a0a4abaa184c99efa5cc4a78147b3f264a6e7e7340c6c1"}, - {file = "bitarray-2.9.1.tar.gz", hash = "sha256:912efbeed6d8b155c8e8c37464f79d75b1de58936c0f29ffb599ce95af5563f2"}, + {file = "bitarray-2.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:917905de565d9576eb20f53c797c15ba88b9f4f19728acabec8d01eee1d3756a"}, + {file = "bitarray-2.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b35bfcb08b7693ab4bf9059111a6e9f14e07d57ac93cd967c420db58ab9b71e1"}, + {file = "bitarray-2.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea1923d2e7880f9e1959e035da661767b5a2e16a45dfd57d6aa831e8b65ee1bf"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0b63a565e8a311cc8348ff1262d5784df0f79d64031d546411afd5dd7ef67d"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf0620da2b81946d28c0b16f3e3704d38e9837d85ee4f0652816e2609aaa4fed"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79a9b8b05f2876c7195a2b698c47528e86a73c61ea203394ff8e7a4434bda5c8"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:345c76b349ff145549652436235c5532e5bfe9db690db6f0a6ad301c62b9ef21"}, + {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e2936f090bf3f4d1771f44f9077ebccdbc0415d2b598d51a969afcb519df505"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f9346e98fc2abcef90b942973087e2462af6d3e3710e82938078d3493f7fef52"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6ec283d4741befb86e8c3ea2e9ac1d17416c956d392107e45263e736954b1f7"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:962892646599529917ef26266091e4cb3077c88b93c3833a909d68dcc971c4e3"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e8da5355d7d75a52df5b84750989e34e39919ec7e59fafc4c104cc1607ab2d31"}, + {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:603e7d640e54ad764d2b4da6b61e126259af84f253a20f512dd10689566e5478"}, + {file = "bitarray-2.9.2-cp310-cp310-win32.whl", hash = "sha256:f00079f8e69d75c2a417de7961a77612bb77ef46c09bc74607d86de4740771ef"}, + {file = "bitarray-2.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:1bb33673e7f7190a65f0a940c1ef63266abdb391f4a3e544a47542d40a81f536"}, + {file = "bitarray-2.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fe71fd4b76380c2772f96f1e53a524da7063645d647a4fcd3b651bdd80ca0f2e"}, + {file = "bitarray-2.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d527172919cdea1e13994a66d9708a80c3d33dedcf2f0548e4925e600fef3a3a"}, + {file = "bitarray-2.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:052c5073bdcaa9dd10628d99d37a2f33ec09364b86dd1f6281e2d9f8d3db3060"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e064caa55a6ed493aca1eda06f8b3f689778bc780a75e6ad7724642ba5dc62f7"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:508069a04f658210fdeee85a7a0ca84db4bcc110cbb1d21f692caa13210f24a7"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4da73ebd537d75fa7bccfc2228fcaedea0803f21dd9d0bf0d3b67fef3c4af294"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb378eaa65cd43098f11ff5d27e48ee3b956d2c00d2d6b5bfc2a09fe183be47"}, + {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d14c790b91f6cbcd9b718f88ed737c78939980c69ac8c7f03dd7e60040c12951"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eea9318293bc0ea6447e9ebfba600a62f3428bea7e9c6d42170ae4f481dbab3"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b76ffec27c7450b8a334f967366a9ebadaea66ee43f5b530c12861b1a991f503"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:76b76a07d4ee611405045c6950a1e24c4362b6b44808d4ad6eea75e0dbc59af4"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c7d16beeaaab15b075990cd26963d6b5b22e8c5becd131781514a00b8bdd04bd"}, + {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60df43e868a615c7e15117a1e1c2e5e11f48f6457280eba6ddf8fbefbec7da99"}, + {file = "bitarray-2.9.2-cp311-cp311-win32.whl", hash = "sha256:e788608ed7767b7b3bbde6d49058bccdf94df0de9ca75d13aa99020cc7e68095"}, + {file = "bitarray-2.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:a23397da092ef0a8cfe729571da64c2fc30ac18243caa82ac7c4f965087506ff"}, + {file = "bitarray-2.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:90e3a281ffe3897991091b7c46fca38c2675bfd4399ffe79dfeded6c52715436"}, + {file = "bitarray-2.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bed637b674db5e6c8a97a4a321e3e4d73e72d50b5c6b29950008a93069cc64cd"}, + {file = "bitarray-2.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e49066d251dbbe4e6e3a5c3937d85b589e40e2669ad0eef41a00f82ec17d844b"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4344e96642e2211fb3a50558feff682c31563a4c64529a931769d40832ca79"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aeb60962ec4813c539a59fbd4f383509c7222b62c3fb1faa76b54943a613e33a"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f7982f10581bb16553719e5e8f933e003f5b22f7d25a68bdb30fac630a6ff"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c71d1cabdeee0cdda4669168618f0e46b7dace207b29da7b63aaa1adc2b54081"}, + {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0ef2d0a6f1502d38d911d25609b44c6cc27bee0a4363dd295df78b075041b60"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6f71d92f533770fb027388b35b6e11988ab89242b883f48a6fe7202d238c61f8"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba0734aa300757c924f3faf8148e1b8c247176a0ac8e16aefdf9c1eb19e868f7"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:d91406f413ccbf4af6ab5ae7bc78f772a95609f9ddd14123db36ef8c37116d95"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:87abb7f80c0a042f3fe8e5264da1a2756267450bb602110d5327b8eaff7682e7"}, + {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b558ce85579b51a2e38703877d1e93b7728a7af664dd45a34e833534f0b755d"}, + {file = "bitarray-2.9.2-cp312-cp312-win32.whl", hash = "sha256:dac2399ee2889fbdd3472bfc2ede74c34cceb1ccf29a339964281a16eb1d3188"}, + {file = "bitarray-2.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:48a30d718d1a6dfc22a49547450107abe8f4afdf2abdcbe76eb9ed88edc49498"}, + {file = "bitarray-2.9.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2c6be1b651fad8f3adb7a5aa12c65b612cd9b89530969af941844ae680f7d981"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5b399ae6ab975257ec359f03b48fc00b1c1cd109471e41903548469b8feae5c"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b3543c8a1cb286ad105f11c25d8d0f712f41c5c55f90be39f0e5a1376c7d0b0"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03adaacb79e2fb8f483ab3a67665eec53bb3fd0cd5dbd7358741aef124688db3"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae5b0657380d2581e13e46864d147a52c1e2bbac9f59b59c576e42fa7d10cf0"}, + {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1f4bf6ea8eb9d7f30808c2e9894237a96650adfecbf5f3643862dc5982f89e"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a8873089be2aa15494c0f81af1209f6e1237d762c5065bc4766c1b84321e1b50"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:677e67f50e2559efc677a4366707070933ad5418b8347a603a49a070890b19bc"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:a620d8ce4ea2f1c73c6b6b1399e14cb68c6915e2be3fad5808c2998ed55b4acf"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:64115ccabbdbe279c24c367b629c6b1d3da9ed36c7420129e27c338a3971bfee"}, + {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d6fb422772e75385b76ad1c52f45a68bd4efafd8be8d0061c11877be74c4d43"}, + {file = "bitarray-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:852e202875dd6dfd6139ce7ec4e98dac2b17d8d25934dc99900831e81c3adaef"}, + {file = "bitarray-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7dfefdcb0dc6a3ba9936063cec65a74595571b375beabe18742b3d91d087eefd"}, + {file = "bitarray-2.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b306c4cf66912511422060f7f5e1149c8bdb404f8e00e600561b0749fdd45659"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a09c4f81635408e3387348f415521d4b94198c562c23330f560596a6aaa26eaf"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5361413fd2ecfdf44dc8f065177dc6aba97fa80a91b815586cb388763acf7f8d"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8a9475d415ef1eaae7942df6f780fa4dcd48fce32825eda591a17abba869299"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b87baa7bfff9a5878fcc1bffe49ecde6e647a72a64b39a69cd8a2992a43a34"}, + {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb6b86cfdfc503e92cb71c68766a24565359136961642504a7cc9faf936d9c88"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd56b8ae87ebc71bcacbd73615098e8a8de952ecbb5785b6b4e2b07da8a06e1f"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3fa909cfd675004aed8b4cc9df352415933656e0155a6209d878b7cb615c787e"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b069ca9bf728e0c5c5b60e00a89df9af34cc170c695c3bfa3b372d8f40288efb"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6067f2f07a7121749858c7daa93c8774325c91590b3e81a299621e347740c2ae"}, + {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:321841cdad1dd0f58fe62e80e9c9c7531f8ebf8be93f047401e930dc47425b1e"}, + {file = "bitarray-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:54e16e32e60973bb83c315de9975bc1bcfc9bd50bb13001c31da159bc49b0ca1"}, + {file = "bitarray-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4dcadb7b8034aa3491ee8f5a69b3d9ba9d7d1e55c3cc1fc45be313e708277f8"}, + {file = "bitarray-2.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c8919fdbd3bb596b104388b56ae4b266eb28da1f2f7dff2e1f9334a21840fe96"}, + {file = "bitarray-2.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb7a9d8a2e400a1026de341ad48e21670a6261a75b06df162c5c39b0d0e7c8f4"}, + {file = "bitarray-2.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6ec84668dd7b937874a2b2c293cd14ba84f37be0d196dead852e0ada9815d807"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2de9a31c34e543ae089fd2a5ced01292f725190e379921384f695e2d7184bd3"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9521f49ae121a17c0a41e5112249e6fa7f6a571245b1118de81fb86e7c1bc1ce"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6cc6545d6d76542aee3d18c1c9485fb7b9812b8df4ebe52c4535ec42081b48f"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:856bbe1616425f71c0df5ef2e8755e878d9504d5a531acba58ab4273c52c117a"}, + {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4bba8042ea6ab331ade91bc435d81ad72fddb098e49108610b0ce7780c14e68"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a035da89c959d98afc813e3c62f052690d67cfd55a36592f25d734b70de7d4b0"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d70b1579da7fb71be5a841a1f965d19aca0ef27f629cfc07d06b09aafd0a333"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:405b83bed28efaae6d86b6ab287c75712ead0adbfab2a1075a1b7ab47dad4d62"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7eb8be687c50da0b397d5e0ab7ca200b5ebb639e79a9f5e285851d1944c94be9"}, + {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eceb551dfeaf19c609003a69a0cf8264b0efd7abc3791a11dfabf4788daf0d19"}, + {file = "bitarray-2.9.2-cp38-cp38-win32.whl", hash = "sha256:bb198c6ed1edbcdaf3d1fa3c9c9d1cdb7e179a5134ef5ee660b53cdec43b34e7"}, + {file = "bitarray-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:648d2f2685590b0103c67a937c2fb9e09bcc8dfb166f0c7c77bd341902a6f5b3"}, + {file = "bitarray-2.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ea816dc8f8e65841a8bbdd30e921edffeeb6f76efe6a1eb0da147b60d539d1cf"}, + {file = "bitarray-2.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4d0e32530f941c41eddfc77600ec89b65184cb909c549336463a738fab3ed285"}, + {file = "bitarray-2.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4a22266fb416a3b6c258bf7f83c9fe531ba0b755a56986a81ad69dc0f3bcc070"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc6d3e80dd8239850f2604833ff3168b28909c8a9357abfed95632cccd17e3e7"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f135e804986b12bf14f2cd1eb86674c47dea86c4c5f0fa13c88978876b97ebe6"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87580c7f7d14f7ec401eda7adac1e2a25e95153e9c339872c8ae61b3208819a1"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64b433e26993127732ac7b66a7821b2537c3044355798de7c5fcb0af34b8296f"}, + {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e497c535f2a9b68c69d36631bf2dba243e05eb343b00b9c7bbdc8c601c6802d"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e40b3cb9fa1edb4e0175d7c06345c49c7925fe93e39ef55ecb0bc40c906b0c09"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f2f8692f95c9e377eb19ca519d30d1f884b02feb7e115f798de47570a359e43f"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f0b84fc50b6dbeced4fa390688c07c10a73222810fb0e08392bd1a1b8259de36"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d656ad38c942e38a470ddbce26b5020e08e1a7ea86b8fd413bb9024b5189993a"}, + {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ab0f1dbfe5070db98771a56aa14797595acd45a1af9eadfb193851a270e7996"}, + {file = "bitarray-2.9.2-cp39-cp39-win32.whl", hash = "sha256:0a99b23ac845a9ea3157782c97465e6ae026fe0c7c4c1ed1d88f759fd6ea52d9"}, + {file = "bitarray-2.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:9bbcfc7c279e8d74b076e514e669b683f77b4a2a328585b3f16d4c5259c91222"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:43847799461d8ba71deb4d97b47250c2c2fb66d82cd3cb8b4caf52bb97c03034"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f44381b0a4bdf64416082f4f0e7140377ae962c0ced6f983c6d7bbfc034040"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a484061616fb4b158b80789bd3cb511f399d2116525a8b29b6334c68abc2310f"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ff9e38356cc803e06134cf8ae9758e836ccd1b793135ef3db53c7c5d71e93bc"}, + {file = "bitarray-2.9.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b44105792fbdcfbda3e26ee88786790fda409da4c71f6c2b73888108cf8f062f"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7e913098de169c7fc890638ce5e171387363eb812579e637c44261460ac00aa2"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6fe315355cdfe3ed22ef355b8bdc81a805ca4d0949d921576560e5b227a1112"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f708e91fdbe443f3bec2df394ed42328fb9b0446dff5cb4199023ac6499e09fd"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b7b09489b71f9f1f64c0fa0977e250ec24500767dab7383ba9912495849cadf"}, + {file = "bitarray-2.9.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:128cc3488176145b9b137fdcf54c1c201809bbb8dd30b260ee40afe915843b43"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:21f21e7f56206be346bdbda2a6bdb2165a5e6a11821f88fd4911c5a6bbbdc7e2"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f4dd3af86dd8a617eb6464622fb64ca86e61ce99b59b5c35d8cd33f9c30603d"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6465de861aff7a2559f226b37982007417eab8c3557543879987f58b453519bd"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbaf2bb71d6027152d603f1d5f31e0dfd5e50173d06f877bec484e5396d4594b"}, + {file = "bitarray-2.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f32948c86e0d230a296686db28191b67ed229756f84728847daa0c7ab7406e3"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be94e5a685e60f9d24532af8fe5c268002e9016fa80272a94727f435de3d1003"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5cc9381fd54f3c23ae1039f977bfd6d041a5c3c1518104f616643c3a5a73b15"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd926e8ae4d1ed1ac4a8f37212a62886292f692bc1739fde98013bf210c2d175"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:461a3dafb9d5fda0bb3385dc507d78b1984b49da3fe4c6d56c869a54373b7008"}, + {file = "bitarray-2.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:393cb27fd859af5fd9c16eb26b1c59b17b390ff66b3ae5d0dd258270191baf13"}, + {file = "bitarray-2.9.2.tar.gz", hash = "sha256:a8f286a51a32323715d77755ed959f94bef13972e9a2fe71b609e40e6d27957e"}, ] [[package]] @@ -348,13 +379,13 @@ files = [ [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -520,6 +551,17 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "chime" +version = "0.7.0" +description = "Python sound notifications made easy." +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "chime-0.7.0-py3-none-any.whl", hash = "sha256:9626f8151cb008b1e0ffb7de6d1834b7013ba5fc4c4e3c9ba6e29dc9bf5feac6"}, + {file = "chime-0.7.0.tar.gz", hash = "sha256:ba4af8934ec8bd9a89a340b4433b2e500097b979823386432be7128e0b201f0d"}, +] + [[package]] name = "click" version = "8.1.7" @@ -547,13 +589,13 @@ files = [ [[package]] name = "comm" -version = "0.2.0" +version = "0.2.1" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." optional = false python-versions = ">=3.8" files = [ - {file = "comm-0.2.0-py3-none-any.whl", hash = "sha256:2da8d9ebb8dd7bfc247adaff99f24dce705638a8042b85cb995066793e391001"}, - {file = "comm-0.2.0.tar.gz", hash = "sha256:a517ea2ca28931c7007a7a99c562a0fa5883cfb48963140cf642c41c948498be"}, + {file = "comm-0.2.1-py3-none-any.whl", hash = "sha256:87928485c0dfc0e7976fd89fc1e187023cf587e7c353e4a9b417555b44adf021"}, + {file = "comm-0.2.1.tar.gz", hash = "sha256:0bc91edae1344d39d3661dcbc36937181fdaddb304790458f8b044dbc064b89a"}, ] [package.dependencies] @@ -564,63 +606,63 @@ test = ["pytest"] [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, - {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, - {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, - {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, - {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, - {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, - {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, - {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, - {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, - {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [package.extras] @@ -628,47 +670,56 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "41.0.7" +version = "42.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"}, - {file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"}, - {file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"}, - {file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"}, - {file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"}, - {file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"}, - {file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"}, - {file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"}, - {file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"}, - {file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, + {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, + {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, + {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, + {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, + {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, + {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, ] [package.dependencies] -cffi = ">=1.12" +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -739,13 +790,13 @@ files = [ [[package]] name = "docutils" -version = "0.18.1" +version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, - {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] @@ -828,6 +879,20 @@ files = [ {file = "fasteners-0.19.tar.gz", hash = "sha256:b4f37c3ac52d8a445af3a66bce57b33b5e90b97c696b7b984f530cf8f0ded09c"}, ] +[[package]] +name = "humanfriendly" +version = "10.0" +description = "Human friendly output for text interfaces using Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, + {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, +] + +[package.dependencies] +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} + [[package]] name = "idna" version = "3.6" @@ -893,13 +958,13 @@ files = [ [[package]] name = "ipykernel" -version = "6.28.0" +version = "6.29.0" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" files = [ - {file = "ipykernel-6.28.0-py3-none-any.whl", hash = "sha256:c6e9a9c63a7f4095c0a22a79f765f079f9ec7be4f2430a898ddea889e8665661"}, - {file = "ipykernel-6.28.0.tar.gz", hash = "sha256:69c11403d26de69df02225916f916b37ea4b9af417da0a8c827f84328d88e5f3"}, + {file = "ipykernel-6.29.0-py3-none-any.whl", hash = "sha256:076663ca68492576f051e4af7720d33f34383e655f2be0d544c8b1c9de915b2f"}, + {file = "ipykernel-6.29.0.tar.gz", hash = "sha256:b5dd3013cab7b330df712891c96cd1ab868c27a7159e606f762015e9bf8ceb3f"}, ] [package.dependencies] @@ -922,7 +987,7 @@ cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] pyqt5 = ["pyqt5"] pyside6 = ["pyside6"] -test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov", "pytest-timeout"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (==0.23.2)", "pytest-cov", "pytest-timeout"] [[package]] name = "ipython" @@ -982,13 +1047,13 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -1022,13 +1087,13 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt [[package]] name = "jupyter-core" -version = "5.5.1" +version = "5.7.1" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_core-5.5.1-py3-none-any.whl", hash = "sha256:220dfb00c45f0d780ce132bb7976b58263f81a3ada6e90a9b6823785a424f739"}, - {file = "jupyter_core-5.5.1.tar.gz", hash = "sha256:1553311a97ccd12936037f36b9ab4d6ae8ceea6ad2d5c90d94a909e752178e40"}, + {file = "jupyter_core-5.7.1-py3-none-any.whl", hash = "sha256:c65c82126453a723a2804aa52409930434598fd9d35091d63dfb919d2b765bb7"}, + {file = "jupyter_core-5.7.1.tar.gz", hash = "sha256:de61a9d7fc71240f688b2fb5ab659fbb56979458dc66a71decd098e03c79e218"}, ] [package.dependencies] @@ -1146,71 +1211,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] @@ -1275,7 +1340,7 @@ test = ["pytest (<5.4)", "pytest-cov"] [[package]] name = "mpremote" -version = "0.4.0.post16+g291b219" +version = "0.4.0.post17+g26093fb" description = "Tool for interacting remotely with MicroPython devices" optional = true python-versions = ">=3.4" @@ -1290,7 +1355,7 @@ pyserial = ">=3.3" type = "git" url = "https://github.com/Josverl/mpremote" reference = "HEAD" -resolved_reference = "291b219ebc753ec71b587dcbe9c591e382b86aef" +resolved_reference = "26093fb35b3f7e4fbde2ceb12d2f1fd6218aea7f" subdirectory = "tools/mpremote" [[package]] @@ -1390,13 +1455,13 @@ testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4, [[package]] name = "nest-asyncio" -version = "1.5.8" +version = "1.6.0" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" files = [ - {file = "nest_asyncio-1.5.8-py3-none-any.whl", hash = "sha256:accda7a339a70599cb08f9dd09a67e0c2ef8d8d6f4c07f96ab203f2ae254e48d"}, - {file = "nest_asyncio-1.5.8.tar.gz", hash = "sha256:25aa2ca0d2a5b5531956b9e273b45cf664cae2b145101d73b86b199978d48fdb"}, + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] [[package]] @@ -1466,13 +1531,13 @@ ptyprocess = ">=0.5" [[package]] name = "pipx" -version = "1.4.0" +version = "1.4.3" description = "Install and Run Python Applications in Isolated Environments" optional = false python-versions = ">=3.8" files = [ - {file = "pipx-1.4.0-py3-none-any.whl", hash = "sha256:31be8329d0678dfdc449e072368b7c1dc6500f3b7e360409e788d12c55189c74"}, - {file = "pipx-1.4.0.tar.gz", hash = "sha256:dbdf88e25ae8964e76ce4efe013a1e50893f22df92fcb0934aadb91653af2074"}, + {file = "pipx-1.4.3-py3-none-any.whl", hash = "sha256:aa25c7a7d455daed7597bb88c259389176680e2e7446fbdb6e6696cdd3a5090b"}, + {file = "pipx-1.4.3.tar.gz", hash = "sha256:d214512bccc601b575de096ee84fde8797323717a20752c48f7a55cc1bf062fe"}, ] [package.dependencies] @@ -1485,28 +1550,28 @@ userpath = ">=1.6,<1.9.0 || >1.9.0" [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1529,27 +1594,27 @@ wcwidth = "*" [[package]] name = "psutil" -version = "5.9.7" +version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6"}, - {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056"}, - {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508"}, - {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df"}, - {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7"}, - {file = "psutil-5.9.7-cp27-none-win32.whl", hash = "sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c"}, - {file = "psutil-5.9.7-cp27-none-win_amd64.whl", hash = "sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6"}, - {file = "psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e"}, - {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284"}, - {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"}, - {file = "psutil-5.9.7-cp36-cp36m-win32.whl", hash = "sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9"}, - {file = "psutil-5.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e"}, - {file = "psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68"}, - {file = "psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414"}, - {file = "psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340"}, - {file = "psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c"}, + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, ] [package.extras] @@ -1593,18 +1658,18 @@ files = [ [[package]] name = "pydantic" -version = "2.5.3" +version = "2.6.1" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.1-py3-none-any.whl", hash = "sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f"}, + {file = "pydantic-2.6.1.tar.gz", hash = "sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.6" +pydantic-core = "2.16.2" typing-extensions = ">=4.6.1" [package.extras] @@ -1612,116 +1677,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.6" +version = "2.16.2" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, - {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, - {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, - {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, - {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, - {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, - {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, - {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, - {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, - {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, - {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, - {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c"}, + {file = "pydantic_core-2.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990"}, + {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b"}, + {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731"}, + {file = "pydantic_core-2.16.2-cp310-none-win32.whl", hash = "sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485"}, + {file = "pydantic_core-2.16.2-cp310-none-win_amd64.whl", hash = "sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f"}, + {file = "pydantic_core-2.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11"}, + {file = "pydantic_core-2.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113"}, + {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8"}, + {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97"}, + {file = "pydantic_core-2.16.2-cp311-none-win32.whl", hash = "sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b"}, + {file = "pydantic_core-2.16.2-cp311-none-win_amd64.whl", hash = "sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc"}, + {file = "pydantic_core-2.16.2-cp311-none-win_arm64.whl", hash = "sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0"}, + {file = "pydantic_core-2.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039"}, + {file = "pydantic_core-2.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb"}, + {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e"}, + {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc"}, + {file = "pydantic_core-2.16.2-cp312-none-win32.whl", hash = "sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d"}, + {file = "pydantic_core-2.16.2-cp312-none-win_amd64.whl", hash = "sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890"}, + {file = "pydantic_core-2.16.2-cp312-none-win_arm64.whl", hash = "sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943"}, + {file = "pydantic_core-2.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ae34418b6b389d601b31153b84dce480351a352e0bb763684a1b993d6be30f17"}, + {file = "pydantic_core-2.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:732bd062c9e5d9582a30e8751461c1917dd1ccbdd6cafb032f02c86b20d2e7ec"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b52776a2e3230f4854907a1e0946eec04d41b1fc64069ee774876bbe0eab55"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef551c053692b1e39e3f7950ce2296536728871110e7d75c4e7753fb30ca87f4"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebb892ed8599b23fa8f1799e13a12c87a97a6c9d0f497525ce9858564c4575a4"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa6c8c582036275997a733427b88031a32ffa5dfc3124dc25a730658c47a572f"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ba0884a91f1aecce75202473ab138724aa4fb26d7707f2e1fa6c3e68c84fbf"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7924e54f7ce5d253d6160090ddc6df25ed2feea25bfb3339b424a9dd591688bc"}, + {file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69a7b96b59322a81c2203be537957313b07dd333105b73db0b69212c7d867b4b"}, + {file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e6231aa5bdacda78e96ad7b07d0c312f34ba35d717115f4b4bff6cb87224f0f"}, + {file = "pydantic_core-2.16.2-cp38-none-win32.whl", hash = "sha256:41dac3b9fce187a25c6253ec79a3f9e2a7e761eb08690e90415069ea4a68ff7a"}, + {file = "pydantic_core-2.16.2-cp38-none-win_amd64.whl", hash = "sha256:f685dbc1fdadb1dcd5b5e51e0a378d4685a891b2ddaf8e2bba89bd3a7144e44a"}, + {file = "pydantic_core-2.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:55749f745ebf154c0d63d46c8c58594d8894b161928aa41adbb0709c1fe78b77"}, + {file = "pydantic_core-2.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b30b0dd58a4509c3bd7eefddf6338565c4905406aee0c6e4a5293841411a1286"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18de31781cdc7e7b28678df7c2d7882f9692ad060bc6ee3c94eb15a5d733f8f7"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5864b0242f74b9dd0b78fd39db1768bc3f00d1ffc14e596fd3e3f2ce43436a33"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8f9186ca45aee030dc8234118b9c0784ad91a0bb27fc4e7d9d6608a5e3d386c"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc6f6c9be0ab6da37bc77c2dda5f14b1d532d5dbef00311ee6e13357a418e646"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa057095f621dad24a1e906747179a69780ef45cc8f69e97463692adbcdae878"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ad84731a26bcfb299f9eab56c7932d46f9cad51c52768cace09e92a19e4cf55"}, + {file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3b052c753c4babf2d1edc034c97851f867c87d6f3ea63a12e2700f159f5c41c3"}, + {file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0f686549e32ccdb02ae6f25eee40cc33900910085de6aa3790effd391ae10c2"}, + {file = "pydantic_core-2.16.2-cp39-none-win32.whl", hash = "sha256:7afb844041e707ac9ad9acad2188a90bffce2c770e6dc2318be0c9916aef1469"}, + {file = "pydantic_core-2.16.2-cp39-none-win_amd64.whl", hash = "sha256:9da90d393a8227d717c19f5397688a38635afec89f2e2d7af0df037f3249c39a"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2"}, + {file = "pydantic_core-2.16.2.tar.gz", hash = "sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06"}, ] [package.dependencies] @@ -1729,13 +1768,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydeps" -version = "1.12.17" +version = "1.12.18" description = "Display module dependencies" optional = false python-versions = "*" files = [ - {file = "pydeps-1.12.17-py3-none-any.whl", hash = "sha256:4fb2e86071c78c1b85a1c63745a267d100e91daf6bab2f14331b3c77433b58b4"}, - {file = "pydeps-1.12.17.tar.gz", hash = "sha256:c308e8355a1e77ff0af899d6f9f1665d4eb07019692dba9fb1dc1cab05df36a4"}, + {file = "pydeps-1.12.18-py3-none-any.whl", hash = "sha256:fc57f56a6eaf92ea6b9b503dc43d55f098661e253a868bbb52fccfbbcc8e79de"}, + {file = "pydeps-1.12.18.tar.gz", hash = "sha256:15c5d023b5053308e19a69591da06d9f3ff038e7a47111c40c9986b6a2929a4b"}, ] [package.dependencies] @@ -1758,33 +1797,43 @@ snowballstemmer = ">=2.2.0" [package.extras] toml = ["tomli (>=1.2.3)"] +[[package]] +name = "pyelftools" +version = "0.30" +description = "Library for analyzing ELF files and DWARF debugging information" +optional = false +python-versions = "*" +files = [ + {file = "pyelftools-0.30-py2.py3-none-any.whl", hash = "sha256:544c3440eddb9a0dce70b6611de0b28163d71def759d2ed57a0d00118fc5da86"}, + {file = "pyelftools-0.30.tar.gz", hash = "sha256:2fc92b0d534f8b081f58c7c370967379123d8e00984deb53c209364efd575b40"}, +] + [[package]] name = "pyflakes" -version = "3.1.0" +version = "3.2.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] [[package]] name = "pygithub" -version = "2.1.1" +version = "2.2.0" description = "Use the full Github API v3" optional = false python-versions = ">=3.7" files = [ - {file = "PyGithub-2.1.1-py3-none-any.whl", hash = "sha256:4b528d5d6f35e991ea5fd3f942f58748f24938805cb7fcf24486546637917337"}, - {file = "PyGithub-2.1.1.tar.gz", hash = "sha256:ecf12c2809c44147bce63b047b3d2e9dac8a41b63e90fcb263c703f64936b97c"}, + {file = "PyGithub-2.2.0-py3-none-any.whl", hash = "sha256:41042ea53e4c372219db708c38d2ca1fd4fadab75475bac27d89d339596cfad1"}, + {file = "PyGithub-2.2.0.tar.gz", hash = "sha256:e39be7c4dc39418bdd6e3ecab5931c636170b8b21b4d26f9ecf7e6102a3b51c3"}, ] [package.dependencies] Deprecated = "*" pyjwt = {version = ">=2.4.0", extras = ["crypto"]} pynacl = ">=1.4.0" -python-dateutil = "*" requests = ">=2.14.0" typing-extensions = ">=4.0.0" urllib3 = ">=1.26.0" @@ -1852,13 +1901,13 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pypi-simple" -version = "1.4.0" +version = "1.4.1" description = "PyPI Simple Repository API client library" optional = false python-versions = ">=3.7" files = [ - {file = "pypi-simple-1.4.0.tar.gz", hash = "sha256:f0a2bce892bf6a878fb0975e245310187e888a2bfb374ecdb6377aa3c3540cd6"}, - {file = "pypi_simple-1.4.0-py3-none-any.whl", hash = "sha256:2d92fe0c0eab03f9515bceacbdb32ca194b31632d992bf26a2634a3bc6d29d5f"}, + {file = "pypi_simple-1.4.1-py3-none-any.whl", hash = "sha256:e0d1b6e1278b825ffb7a5c01b497a2c6ebf1f63d0aa36bf7d7bbaf101a70b987"}, + {file = "pypi_simple-1.4.1.tar.gz", hash = "sha256:8bd1c303ef5c9b6736fd7a6a478fba4d10b405d668e56a64ad51c115053325df"}, ] [package.dependencies] @@ -1871,15 +1920,26 @@ requests = ">=2.20,<3.0" [package.extras] tqdm = ["tqdm"] +[[package]] +name = "pyreadline3" +version = "3.4.1" +description = "A python implementation of GNU readline." +optional = false +python-versions = "*" +files = [ + {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, + {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, +] + [[package]] name = "pyright" -version = "1.1.343" +version = "1.1.349" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.343-py3-none-any.whl", hash = "sha256:75a0d24e8227328198bdfa1f4904ce66b78c2bacf49c269d9e6e3b174b026225"}, - {file = "pyright-1.1.343.tar.gz", hash = "sha256:871e122d74003e8e5fddb17867220b06ee892de61fa967ca7ca031acdc176738"}, + {file = "pyright-1.1.349-py3-none-any.whl", hash = "sha256:8f9189ddb62222a35b3525666225f1d8f24244cbff5893c42b3f001d8ebafa1a"}, + {file = "pyright-1.1.349.tar.gz", hash = "sha256:af4ab7f103a0b2a92e5fbf248bf734e9a98247991350ac989ead34e97148f91c"}, ] [package.dependencies] @@ -1921,13 +1981,13 @@ ujson = ["ujson (==5.2.0)"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -1972,13 +2032,13 @@ pytest-metadata = "*" [[package]] name = "pytest-metadata" -version = "3.0.0" +version = "3.1.0" description = "pytest plugin for test session metadata" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest_metadata-3.0.0-py3-none-any.whl", hash = "sha256:a17b1e40080401dc23177599208c52228df463db191c1a573ccdffacd885e190"}, - {file = "pytest_metadata-3.0.0.tar.gz", hash = "sha256:769a9c65d2884bd583bc626b0ace77ad15dbe02dd91a9106d47fd46d9c2569ca"}, + {file = "pytest_metadata-3.1.0-py3-none-any.whl", hash = "sha256:54ce21108708d0f2fdb30c7056ce5789ce052262efff4832892aa92df4a76291"}, + {file = "pytest_metadata-3.1.0.tar.gz", hash = "sha256:53dcd8bbd101cf6ca97efb4a42a72fcbee5de173bddad4850f4ce9bb27e9f0f2"}, ] [package.dependencies] @@ -2020,13 +2080,13 @@ six = ">=1.5" [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [package.extras] @@ -2262,6 +2322,43 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "13.7.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rich-click" +version = "1.7.3" +description = "Format click help output nicely with rich" +optional = false +python-versions = ">=3.7" +files = [ + {file = "rich-click-1.7.3.tar.gz", hash = "sha256:bced1594c497dc007ab49508ff198bb437c576d01291c13a61658999066481f4"}, + {file = "rich_click-1.7.3-py3-none-any.whl", hash = "sha256:bc4163d4e2a3361e21c4d72d300eca6eb8896dfc978667923cb1d4937b8769a3"}, +] + +[package.dependencies] +click = ">=7" +rich = ">=10.7.0" +typing-extensions = "*" + +[package.extras] +dev = ["flake8", "flake8-docstrings", "mypy", "packaging", "pre-commit", "pytest", "pytest-cov", "types-setuptools"] + [[package]] name = "setuptools" version = "69.0.3" @@ -2404,56 +2501,50 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.7" +version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"}, - {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"}, + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" -version = "1.0.5" +version = "1.0.6" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"}, - {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"}, + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.4" +version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"}, - {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"}, + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] @@ -2497,38 +2588,34 @@ files = [ [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.6" +version = "1.0.7" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"}, - {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"}, + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" -version = "1.1.9" +version = "1.1.10" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"}, - {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"}, + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] @@ -2640,13 +2727,13 @@ files = [ [[package]] name = "traitlets" -version = "5.14.0" +version = "5.14.1" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.14.0-py3-none-any.whl", hash = "sha256:f14949d23829023013c47df20b4a76ccd1a85effb786dc060f34de7948361b33"}, - {file = "traitlets-5.14.0.tar.gz", hash = "sha256:fcdaa8ac49c04dfa0ed3ee3384ef6dfdb5d6f3741502be247279407679296772"}, + {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, + {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, ] [package.extras] @@ -2692,17 +2779,18 @@ typing-extensions = ">=3.7.4" [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, + {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2722,13 +2810,13 @@ click = "*" [[package]] name = "wcwidth" -version = "0.2.12" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, - {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] [[package]] @@ -2845,4 +2933,4 @@ tools = ["esptool"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "991f6fdc1f690fa9fa0e8e7bdf5c263ec28b3ec6fcab4c66566da4d175555ccb" +content-hash = "ebc60c2e50d2251fa77dcd9efa7937c36688d8eec86b79b760c530eb5605424d" diff --git a/pyproject.toml b/pyproject.toml index af46a8893..496ee58c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ repo-path = "./repos" [tool.poetry] name = "micropython-stubber" -version = "1.16.2" +version = "1.17.0" description = "Tooling to create and maintain stubs for MicroPython" authors = ["Jos Verlinde "] license = "MIT" @@ -71,6 +71,7 @@ typed-config = "^1.3.0" pyserial = "^3.5" executing = "^2.0.1" mypy-gitlab-code-quality = "^1.1.0" +rich-click = "^1.7.3" [tool.poetry.extras] tools = ["esptool"] @@ -102,6 +103,8 @@ ipykernel = "^6.23.1" fasteners = "^0.19" python-dotenv = "^1.0.0" pydocstyle = "^6.3.0" +chime = "^0.7.0" +bincopy = "^20.0.0" @@ -260,7 +263,7 @@ markers = [ [tool.coverage.run] parallel = false branch = true -source = ["board", "src"] +source = ["src"] omit = [ # helper files in board "*/boot.py", diff --git a/requirements-stubs.txt b/requirements-stubs.txt new file mode 100644 index 000000000..15a0235dc --- /dev/null +++ b/requirements-stubs.txt @@ -0,0 +1,4 @@ +# install the stusbs using +# pip install -r requirements-stubs.txt -t typings --no-user + +micropython-esp32-stubs diff --git a/scripts/Docstrings_check.ipynb b/scripts/Docstrings_check.ipynb index 5468b3186..4df60bb83 100644 --- a/scripts/Docstrings_check.ipynb +++ b/scripts/Docstrings_check.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -58,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -109,7 +109,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -674,7 +674,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "168\n" + "197\n" ] } ], @@ -732,7 +732,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -753,7 +753,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -769,7 +769,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -782,7 +782,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -839,20 +839,20 @@ " 1.00\n", " 0.96\n", " 0.97\n", - " {'def': 911, 'class': 116, 'module': 55}\n", + " {'def': 919, 'class': 120, 'module': 55}\n", " {'module': 0, 'class': 5, 'def': 24}\n", " \n", " \n", " 2\n", - " micropython-latest-esp32--OLD\n", + " micropython-latest-esp32-ESP32_GENERIC-merged\n", " ..\\repos\\micropython-stubs\\stubs\\micropython-l...\n", " 99.99.99\n", " esp32\n", - " 0.52\n", - " 0.29\n", - " 0.11\n", - " {'def': 2100, 'class': 331, 'module': 170}\n", - " {'module': 81, 'class': 234, 'def': 1874}\n", + " 0.84\n", + " 0.71\n", + " 0.59\n", + " {'def': 2248, 'class': 385, 'module': 174}\n", + " {'module': 28, 'class': 110, 'def': 918}\n", " \n", " \n", " 3\n", @@ -883,12 +883,12 @@ "" ], "text/plain": [ - " folder \\\n", - "0 micropython-core \n", - "1 micropython-latest-docstubs \n", - "2 micropython-latest-esp32--OLD \n", - "3 micropython-latest-esp32-merged \n", - "4 micropython-latest-esp8266-merged \n", + " folder \\\n", + "0 micropython-core \n", + "1 micropython-latest-docstubs \n", + "2 micropython-latest-esp32-ESP32_GENERIC-merged \n", + "3 micropython-latest-esp32-merged \n", + "4 micropython-latest-esp8266-merged \n", "\n", " path version port \\\n", "0 ..\\repos\\micropython-stubs\\stubs\\micropython-core core unknown \n", @@ -899,20 +899,20 @@ "\n", " module class def counts \\\n", "0 1.00 1.00 1.00 {'def': 14, 'class': 1, 'module': 2} \n", - "1 1.00 0.96 0.97 {'def': 911, 'class': 116, 'module': 55} \n", - "2 0.52 0.29 0.11 {'def': 2100, 'class': 331, 'module': 170} \n", + "1 1.00 0.96 0.97 {'def': 919, 'class': 120, 'module': 55} \n", + "2 0.84 0.71 0.59 {'def': 2248, 'class': 385, 'module': 174} \n", "3 0.83 0.70 0.58 {'def': 2236, 'class': 377, 'module': 174} \n", "4 0.89 0.80 0.62 {'def': 1322, 'class': 166, 'module': 116} \n", "\n", - " missing \n", - "0 {'module': 0, 'class': 0, 'def': 0} \n", - "1 {'module': 0, 'class': 5, 'def': 24} \n", - "2 {'module': 81, 'class': 234, 'def': 1874} \n", - "3 {'module': 30, 'class': 112, 'def': 950} \n", - "4 {'module': 13, 'class': 34, 'def': 498} " + " missing \n", + "0 {'module': 0, 'class': 0, 'def': 0} \n", + "1 {'module': 0, 'class': 5, 'def': 24} \n", + "2 {'module': 28, 'class': 110, 'def': 918} \n", + "3 {'module': 30, 'class': 112, 'def': 950} \n", + "4 {'module': 13, 'class': 34, 'def': 498} " ] }, - "execution_count": 8, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -920,13 +920,6 @@ "source": [ "df.head()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -945,7 +938,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/scripts/board_stubber.py b/scripts/board_stubber.py index cb3f95433..dfd6d30ad 100644 --- a/scripts/board_stubber.py +++ b/scripts/board_stubber.py @@ -1,11 +1,5 @@ """ -This script creates stubs for a connected micropython MCU board. - -# MPRemote is not working properly with ESP32 boards :-( at least on Windows) -# this was fixed in the latest version of mpremote, not published yet on pypi - -Workaround -pip install git+https://github.com/josverl/mpremote.git#subdirectory=tools/mpremote +This script creates stubs on and for a connected micropython MCU board. """ import json @@ -20,13 +14,16 @@ from threading import Timer from typing import List, NamedTuple, Optional, Tuple, Union +import rich_click as click import serial.tools.list_ports from loguru import logger as log -from tabulate import tabulate +from rich.console import Console +from rich.table import Table from tenacity import retry, stop_after_attempt, wait_fixed from stubber import utils from stubber.publish.merge_docstubs import get_board_path, merge_all_docstubs +from stubber.publish.publish import build_multiple from stubber.utils.config import CONFIG OK = 0 @@ -36,6 +33,8 @@ LOCAL_FILES = True ############################################################################################### +reset_before = True +############################################################################################### @dataclass @@ -86,7 +85,7 @@ def run( if not error_tags: error_tags = ["Traceback ", "Error: ", "Exception: ", "ERROR :", "CRIT :"] if not warning_tags: - warning_tags = ["WARN :"] # , "Module not found." + warning_tags = ["WARN :", "TRACE :"] # , "Module not found." if not success_tags: success_tags = ["Created stubs for", "Path: /remote"] if not ignore_tags: @@ -97,7 +96,11 @@ def run( output = [] try: proc = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding="utf-8", ) except FileNotFoundError as e: raise FileNotFoundError(f"Failed to start {cmd[0]}") from e @@ -142,13 +145,11 @@ def timed_out(): timer.cancel() proc.wait(timeout=1) - return proc.returncode, output + return proc.returncode or 0, output ############################################################################################### -UName = NamedTuple("UName", sysname=str, nodename=str, release=str, version=str, machine=str) - class Variant(str, Enum): """Variants of generatings stubs on a MCU""" @@ -169,13 +170,17 @@ class Form(str, Enum): class MPRemoteBoard: """Class to run mpremote commands""" - def __init__(self, port: str = ""): - self.port = port + def __init__(self, serialport: str = ""): + self.serialport = serialport # self.board = "" self.firmware = {} - self.uname: Optional[UName] = None + self.connected = False self.path: Optional[Path] = None + self.description = "" + self.version = "" + self.port = "" + self.board = "" @staticmethod def connected_boards(): @@ -184,30 +189,41 @@ def connected_boards(): return sorted(devices) @retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(1)) - def get_uname(self): + def get_mcu_info(self): rc, result = self.run_command( - ["exec", "import os;print(os.uname() if 'uname' in dir(os) else 'no.uname')"], + ["run", "src/stubber/board/fw_info.py"], no_info=True, ) - s = result[0] - if "sysname=" in s: - self.uname = eval(f"UName{s}") - elif s.strip() == "no.uname": - self.uname = UName("no.uname", "?", "?", "?", "?") - else: - self.uname = UName(*s[1:-1].split(", ")) - self.connected = True - return rc, self.uname + if rc != OK: + raise RuntimeError(f"Failed to get mcu_info for {self.serialport}") + # Ok we have the info, now parse it + s = result[0].strip() + if s.startswith("{") and s.endswith("}"): + info = eval(s) + self.version = info["version"] + self.port = info["port"] + self.description = descr = info["board"] + pos = descr.rfind(" with") + if pos != -1: + short_descr = descr[:pos].strip() + else: + short_descr = "" + if board_name := find_board( + descr, short_descr, Path(__file__).parent.parent / "src/stubber/data/board_info.csv" + ): + self.board = board_name + else: + self.board = "UNKNOWN" def disconnect(self) -> bool: """Disconnect from a board""" if not self.connected: return True - if not self.port: + if not self.serialport: log.error("No port connected") self.connected = False return False - log.info(f"Disconnecting from {self.port}") + log.info(f"Disconnecting from {self.serialport}") result = self.run_command(["disconnect"])[0] == OK self.connected = False return result @@ -236,14 +252,14 @@ def run_command( """ if isinstance(cmd, str): cmd = cmd.split(" ") - prefix = ["mpremote", "connect", self.port] if self.port else ["mpremote"] + prefix = [sys.executable, "-m", "mpremote", "connect", self.serialport] if self.serialport else ["mpremote"] # if connected add resume to keep state between commands if self.connected: prefix += ["resume"] cmd = prefix + cmd log.debug(" ".join(cmd)) result = run(cmd, timeout, log_errors, no_info, **kwargs) - self.connected = True + self.connected = result[0] == OK return result @retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(1)) @@ -256,12 +272,11 @@ def mip_install(self, name: str) -> bool: return result -def copy_createstubs(board: MPRemoteBoard, variant: Variant, form: Form) -> bool: +def copy_createstubs_to_board(board: MPRemoteBoard, variant: Variant, form: Form) -> bool: # sourcery skip: assign-if-exp, boolean-if-exp-identity, remove-unnecessary-cast """Copy createstubs to the board""" # copy createstubs.py to the destination folder origin = "./src/stubber/board" - # origin = "./micropython-stubber-1.7.0/minified" _py = [ "rm :lib/createstubs.mpy", @@ -273,8 +288,8 @@ def copy_createstubs(board: MPRemoteBoard, variant: Variant, form: Form) -> bool f"cp {origin}/createstubs.py :lib/createstubs.py", f"cp {origin}/createstubs_mem.py :lib/createstubs_mem.py", f"cp {origin}/createstubs_db.py :lib/createstubs_db.py", - f"cp {origin}/logging.py :lib/logging.py", ] + # copy createstubs*_min.py to the destination folder _min = [ f"cp {origin}/createstubs_min.py :lib/createstubs.py", @@ -300,9 +315,8 @@ def copy_createstubs(board: MPRemoteBoard, variant: Variant, form: Form) -> bool _get_ready = [ "rm :modulelist.done", - # "rm :lib/modulelist.txt", + "rm :no_auto_stubber.txt", f"cp {origin}/modulelist.txt :lib/modulelist.txt", - # "cp ./board_info.csv :lib/board_info.csv", ] if form == Form.py: do = _lib + _py + _get_ready @@ -327,13 +341,14 @@ def copy_createstubs(board: MPRemoteBoard, variant: Variant, form: Form) -> bool @retry(stop=stop_after_attempt(4), wait=wait_fixed(2)) def hard_reset(board: MPRemoteBoard) -> bool: """Reset the board""" - rc, _ = board.run_command(["soft-reset", "exec", "import machine;machine.reset()"], timeout=5) + # do not run "exec", "import machine;machine.reset()" as it will hang an esp32 + rc, _ = board.run_command(["reset"], timeout=5) board.connected = False return rc == OK @retry(stop=stop_after_attempt(10), wait=wait_fixed(15)) -def run_createstubs(dest: Path, board: MPRemoteBoard, variant: Variant = Variant.db): +def run_createstubs(dest: Path, mcu: MPRemoteBoard, variant: Variant = Variant.db): """ Run a createstubs[variant] on the provided board. Retry running the command up to 10 times, with a 15 second timeout between retries. @@ -344,30 +359,24 @@ def run_createstubs(dest: Path, board: MPRemoteBoard, variant: Variant = Variant "exec", 'import sys;sys.path.append("/lib") if "/lib" not in sys.path else "/lib already in path"', ] - board.run_command(cmd_path, timeout=5) - - log.info(f"Resetting {board.port} {board.uname[4] if board.uname else ''}") - board.run_command("reset", timeout=5) + mcu.run_command(cmd_path, timeout=5) - time.sleep(2) + if reset_before: + log.info(f"Resetting {mcu.serialport} {mcu.description}") + mcu.run_command("reset", timeout=5) + time.sleep(2) - log.info( - f"Running createstubs {variant} on {board.port} {board.uname[4] if board.uname else ''}" - ) + log.info(f"Running createstubs {variant.value} on {mcu.serialport} {mcu.description} using temp path: {dest}") cmd = build_cmd(dest, variant) - - board.run_command.retry.wait = wait_fixed(15) + log.info(f"Running : mpremote {' '.join(cmd)}") + mcu.run_command.retry.wait = wait_fixed(15) # some boards need 2-3 minutes to run createstubs - so increase the default timeout - # but slows down esp8266 restarts so keep that to 60 seconds - timeout = 60 if board.uname.nodename == "esp8266" else 4 * 60 - rc, out = board.run_command(cmd, timeout=timeout) + # esp32s3 > 240 seconds with mounted fs + # but slows down esp8266 restarts so keep that to 90 seconds + timeout = 90 if mcu.port == "esp8266" else 6 * 60 # type: ignore + rc, out = mcu.run_command(cmd, timeout=timeout) # check last line for exception or error and raise that if found - if ( - rc != OK - and ":" in out[-1] - and not out[-1].startswith("INFO") - and not out[-1].startswith("WARN") - ): + if rc != OK and out and ":" in out[-1] and not out[-1].startswith("INFO") and not out[-1].startswith("WARN"): log.warning(f"createstubs: {out[-1]}") raise RuntimeError(out[-1]) from eval(out[-1].split(":")[0]) @@ -390,10 +399,14 @@ def build_cmd(dest: Path, variant: Variant = Variant.db): def generate_board_stubs( - dest: Path, mcu: MPRemoteBoard, variant: Variant = Variant.db, form: Form = Form.mpy + dest: Path, + mcu: MPRemoteBoard, + variant: Variant = Variant.db, + form: Form = Form.mpy, + host_mounted=True, ) -> Tuple[int, Optional[Path]]: """ - Generate the board stubs. + Generate the board stubs for this MCU board. Parameters ---------- dest : Path @@ -402,29 +415,24 @@ def generate_board_stubs( The port the board is connected to """ - # board_info_path = Path(__file__).parent.parent / "board_info.csv" - board_info_path = Path(__file__).parent.parent / "src/stubber/data/board_info.csv" # HOST -> MCU : copy createstubs to board - if LOCAL_FILES: - ok = copy_createstubs(mcu, variant, form) - else: - # TODO: Add Branch to install from - ok = mcu.mip_install("github:josverl/micropython-stubber") + ok = copy_scripts_to_board(mcu, variant, form) if not ok and not TESTING: log.warning("Error copying createstubs to board") return ERROR, None - # HOST: remove .done file - (dest / "modulelist.done").unlink(missing_ok=True) - # HOST: copy board_info.csv to destination - shutil.copyfile(board_info_path, dest / "board_info.csv") - # MCU: add lib to path - rc, out = run_createstubs(dest, mcu, variant) + copy_boardname_to_board(mcu) + + rc, out = run_createstubs(dest, mcu, variant) # , host_mounted=host_mounted) if rc != OK: log.warning("Error running createstubs: %s", out) return ERROR, None + if not host_mounted: + # Waiting for MPRemote to support copying folder from board to host + raise NotImplementedError("TODO: Copy stubs from board to host") + # Find the output starting with 'Path: ' folder = get_stubfolder(out) if not folder: @@ -432,13 +440,13 @@ def generate_board_stubs( stubs_path = dest / folder mcu.path = stubs_path - # read the modles.json file into a dict + # read the modules.json file into a dict try: with open(stubs_path / "modules.json") as fp: modules_json = json.load(fp) mcu.firmware = modules_json["firmware"] except FileNotFoundError: - log.warning("Error generating stubs, modules.json not found") + log.warning("Could not load modules.json, Assuming error in createstubs") return ERROR, None # check the number of stubs generated @@ -446,26 +454,62 @@ def generate_board_stubs( log.warning("Error generating stubs, too few (<10)stubs were generated") return ERROR, None - utils.do_post_processing([stubs_path], stubgen=True, black=True, autoflake=True) + stubgen_needed = any(stubs_path.glob("*.py")) + utils.do_post_processing([stubs_path], stubgen=stubgen_needed, black=True, autoflake=True) return OK, stubs_path +def copy_boardname_to_board(mcu: MPRemoteBoard): + """ + Copies the board name to the board by writing it to the 'boardname.py' file. + + Args: + mcu: The MCU object representing the microcontroller. + + Returns: + None + """ + if mcu.board: + cmd = ["exec", f"with open('lib/boardname.py', 'w') as f: f.write('BOARDNAME=\"{mcu.board}\"')"] + log.info(f"Writing BOARDNAME='{mcu.board}' to boardname.py") + else: + cmd = ["rm", "boardname.py"] + rc, _ = mcu.run_command(cmd) + if rc != OK and "rm" not in cmd: + log.error(f"Error during copy createstubs running command: {cmd}") + + +def copy_scripts_to_board(mcu: MPRemoteBoard, variant: Variant, form: Form): + """ + Copy scripts to the board. + + Args: + mcu (str): The microcontroller unit. + variant (str): The variant of the board. + form (Form): The form of the scripts to be copied. + + Returns: + bool: True if the scripts are successfully copied, False otherwise. + """ + if LOCAL_FILES: + return copy_createstubs_to_board(mcu, variant, form) + if form == Form.min: + location = "github:josverl/micropython-stubber/mip/minified.json" + elif form == Form.mpy: + location = "github:josverl/micropython-stubber/mip/mpy_v6.json" + else: + location = "github:josverl/micropython-stubber/mip/full.json" + + return mcu.mip_install(location) + + def get_stubfolder(out: List[str]): return ( - lines[-1].split("/remote/")[-1].strip() - if (lines := [l for l in out if l.startswith("Path: ")]) - else "" + lines[-1].split("/remote/")[-1].strip() if (lines := [l for l in out if l.startswith("INFO : Path: ")]) else "" ) -# def get_port_board(out: List[str]): -# return ( -# lines[-1].split("Port:")[-1].strip() if (lines := [l for l in out if l.startswith("Port: ")]) else "", -# lines[-1].split("Board:")[-1].strip() if (lines := [l for l in out if l.startswith("Board: ")]) else "", -# ) - - def scan_boards(optimistic: bool = False) -> List[MPRemoteBoard]: """ This function scans for boards and returns a list of MPRemoteBoard objects. @@ -474,13 +518,13 @@ def scan_boards(optimistic: bool = False) -> List[MPRemoteBoard]: boards = [] for mpr_port in MPRemoteBoard.connected_boards(): board = MPRemoteBoard(mpr_port) - log.info(f"Attempt to connect to: {board.port}") + log.debug(f"Attempt to connect to: {board.serialport}") try: - _, uname = board.get_uname() - log.success(f"Detected board {uname.machine} {uname.release}") + board.get_mcu_info() + log.success(f"Detected board {board.description} {board.version}") boards.append(board) except Exception: - log.error(f"Failed to get uname for {board.port}") + log.error(f"Failed to get mcu_info for {board.serialport}") if optimistic: boards.append(board) continue @@ -501,9 +545,7 @@ def set_loglevel(verbose: int) -> str: else: format_str = "{time:YYYY-MM-DD HH:mm:ss.SSS}|{level: <8}|{name}:{function}:{line} - {message}" - log.add( - sys.stderr, level=level, backtrace=True, diagnose=True, colorize=True, format=format_str - ) + log.add(sys.stderr, level=level, backtrace=True, diagnose=True, colorize=True, format=format_str) # log.info(f"micropython-stubber {__version__}") return level @@ -527,16 +569,72 @@ def copy_to_repo(source: Path, fw: dict) -> Optional[Path]: return None -if __name__ == "__main__": - set_loglevel(0) +def find_board(descr: str, short_descr: str, filename: Path) -> Optional[str]: + "Find the board in the provided board_info.csv file" + short_hit = "" + with open(filename, "r") as file: + # ugly code to make testable in python and micropython + # TODO: This is VERY slow on micropython whith MPREMOTE mount on esp32 (2-3 minutes to read file) + while 1: + line = file.readline() + if not line: + break + descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() + if descr_ == descr: + return board_ + elif short_descr and descr_ == short_descr: + if "with" in short_descr: + # Good enough - no need to trawl the entire file + # info["board"] = board_ + return board_ + # good enough if not found in the rest of the file (but slow) + short_hit = board_ + if short_hit: + return short_hit + return None + + +@click.command() +@click.option( + "--variant", + "-v", + type=click.Choice(["Full", "Mem", "DB"], case_sensitive=False), + default="Full", + show_default=True, + help="Variant of createstubs to run", +) +@click.option( + "--format", + "-f", + type=click.Choice(["py", "mpy"], case_sensitive=False), + default="py", + show_default=True, + help="Python source or pre-compiled.", +) +@click.option("--debug/--no-debug", default=False, show_default=True, help="Debug mode.") +def run_stubber_connected_boards(variant: str, format: str, debug: bool): + """ + Runs the stubber to generate stubs for connected MicroPython boards. + + Args: + variant (str): The variant of the MicroPython board. + format (str): The format of the generated stubs. + debug (bool): Flag indicating whether to enable debug mode. + + Returns: + None + """ + + if debug: + set_loglevel(1) + else: + set_loglevel(0) + variant = Variant(variant.lower()) + form = Form(format.lower()) - variant = Variant.db - form = Form.mpy tempdir = mkdtemp(prefix="board_stubber") - dest = Path(tempdir) - # copy board_info.csv to the folder - # shutil.copyfile(Path("board_info.csv"), dest / "board_info.csv") + temp_path = Path(tempdir) # scan boards and just work with the ones that reponded with understandable data connected_boards = scan_boards(True) @@ -544,12 +642,26 @@ def copy_to_repo(source: Path, fw: dict) -> Optional[Path]: log.error("No micropython boards were found") sys.exit(1) - print(tabulate([[b.port] + (list(b.uname) if b.uname else ["unable to connect"]) for b in connected_boards])) # type: ignore - # scan boards and generate stubs + table = Table(show_header=True, header_style="bold magenta") + table.add_column("Serial Port") + table.add_column("Port") + table.add_column("Description") + table.add_column("Version") + + for b in connected_boards: + table.add_row(b.serialport, b.port, b.description, b.version) + console = Console() + console.print(table) + # scan boards and generate stubs for board in connected_boards: - log.info(f"Connecting to {board.port} {board.uname[4] if board.uname else ''}") - rc, my_stubs = generate_board_stubs(dest, board, variant, form) + log.info( + f"Connecting using {board.serialport} to {board.port} {board.board} {board.version}: {board.description}" + ) + # remove the modulelist.done file before starting createstubs on each board + (temp_path / "modulelist.done").unlink(missing_ok=True) + + rc, my_stubs = generate_board_stubs(temp_path, board, variant, form) if rc == OK and my_stubs: log.success(f'Stubs generated for {board.firmware["port"]}-{board.firmware["board"]}') if destination := copy_to_repo(my_stubs, board.firmware): @@ -557,16 +669,30 @@ def copy_to_repo(source: Path, fw: dict) -> Optional[Path]: # Also merge the stubs with the docstubs log.info(f"Merging stubs with docstubs : {board.firmware}") - _ = merge_all_docstubs( + merged = merge_all_docstubs( versions=board.firmware["version"], family=board.firmware["family"], boards=board.firmware["board"], ports=board.firmware["port"], mpy_path=CONFIG.mpy_path, ) + if not merged: + log.error(f"Failed to merge stubs for {board.serialport}") + continue + # Then Build the package + log.info(f"Building package for {board.firmware}") + built = build_multiple( + versions=board.firmware["version"], + family=board.firmware["family"], + boards=board.firmware["board"], + ports=board.firmware["port"], + ) + # create a rich table of the results and print it' + console.print(table) log.success("Done") + else: + log.error(f"Failed to generate stubs for {board.serialport}") - # Then Build the package - else: - log.error(f"Failed to generate stubs for {board.port}") +if __name__ == "__main__": + run_stubber_connected_boards() diff --git a/scripts/docstrings.csv b/scripts/docstrings.csv index 9de98802a..d9c464e7c 100644 --- a/scripts/docstrings.csv +++ b/scripts/docstrings.csv @@ -1,18 +1,21 @@ folder,path,version,port,module,class,def,counts,missing micropython-core,..\repos\micropython-stubs\stubs\micropython-core,core,unknown,1.0,1.0,1.0,"{'def': 14, 'class': 1, 'module': 2}","{'module': 0, 'class': 0, 'def': 0}" -micropython-latest-docstubs,..\repos\micropython-stubs\stubs\micropython-latest-docstubs,99.99.99,docstubs,1.0,0.96,0.97,"{'def': 911, 'class': 116, 'module': 55}","{'module': 0, 'class': 5, 'def': 24}" -micropython-latest-esp32--OLD,..\repos\micropython-stubs\stubs\micropython-latest-esp32--OLD,99.99.99,esp32,0.52,0.29,0.11,"{'def': 2100, 'class': 331, 'module': 170}","{'module': 81, 'class': 234, 'def': 1874}" +micropython-latest-docstubs,..\repos\micropython-stubs\stubs\micropython-latest-docstubs,99.99.99,docstubs,1.0,0.96,0.97,"{'def': 919, 'class': 120, 'module': 55}","{'module': 0, 'class': 5, 'def': 24}" +micropython-latest-esp32-ESP32_GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-latest-esp32-ESP32_GENERIC-merged,99.99.99,esp32,0.84,0.71,0.59,"{'def': 2248, 'class': 385, 'module': 174}","{'module': 28, 'class': 110, 'def': 918}" micropython-latest-esp32-merged,..\repos\micropython-stubs\stubs\micropython-latest-esp32-merged,99.99.99,esp32,0.83,0.7,0.58,"{'def': 2236, 'class': 377, 'module': 174}","{'module': 30, 'class': 112, 'def': 950}" micropython-latest-esp8266-merged,..\repos\micropython-stubs\stubs\micropython-latest-esp8266-merged,99.99.99,esp8266,0.89,0.8,0.62,"{'def': 1322, 'class': 166, 'module': 116}","{'module': 13, 'class': 34, 'def': 498}" -micropython-latest-frozen,..\repos\micropython-stubs\stubs\micropython-latest-frozen,99.99.99,frozen,0.14,0.12,0.33,"{'def': 19581, 'class': 2971, 'module': 2327}","{'module': 2004, 'class': 2626, 'def': 13158}" +micropython-latest-frozen,..\repos\micropython-stubs\stubs\micropython-latest-frozen,99.99.99,frozen,0.26,0.17,0.37,"{'def': 20461, 'class': 3084, 'module': 2394}","{'module': 1778, 'class': 2555, 'def': 12977}" micropython-latest-rp2-merged,..\repos\micropython-stubs\stubs\micropython-latest-rp2-merged,99.99.99,rp2,0.91,0.8,0.57,"{'def': 1546, 'class': 359, 'module': 116}","{'module': 11, 'class': 73, 'def': 660}" micropython-latest-rp2-PIMORONI_PICOLIPO_16MB-merged,..\repos\micropython-stubs\stubs\micropython-latest-rp2-PIMORONI_PICOLIPO_16MB-merged,99.99.99,rp2,0.91,0.76,0.57,"{'def': 1546, 'class': 305, 'module': 116}","{'module': 11, 'class': 73, 'def': 660}" +micropython-latest-rp2-RPI_PICO-merged,..\repos\micropython-stubs\stubs\micropython-latest-rp2-RPI_PICO-merged,99.99.99,rp2,0.89,0.82,0.62,"{'def': 1688, 'class': 432, 'module': 128}","{'module': 14, 'class': 79, 'def': 644}" +micropython-latest-rp2-RPI_PICO_W-merged,..\repos\micropython-stubs\stubs\micropython-latest-rp2-RPI_PICO_W-merged,99.99.99,rp2,0.83,0.75,0.57,"{'def': 2288, 'class': 636, 'module': 174}","{'module': 30, 'class': 159, 'def': 978}" micropython-latest-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged,..\repos\micropython-stubs\stubs\micropython-latest-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged,99.99.99,samd,0.88,0.84,0.56,"{'def': 1454, 'class': 397, 'module': 102}","{'module': 12, 'class': 64, 'def': 646}" micropython-latest-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged,..\repos\micropython-stubs\stubs\micropython-latest-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged,99.99.99,samd,0.88,0.83,0.56,"{'def': 1454, 'class': 371, 'module': 102}","{'module': 12, 'class': 64, 'def': 646}" micropython-latest-samd-MINISAM_M4-merged,..\repos\micropython-stubs\stubs\micropython-latest-samd-MINISAM_M4-merged,99.99.99,samd,0.88,0.82,0.56,"{'def': 1454, 'class': 353, 'module': 102}","{'module': 12, 'class': 64, 'def': 646}" -micropython-latest-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-latest-samd-SEEED_WIO_TERMINAL-merged,99.99.99,samd,0.88,0.88,0.56,"{'def': 1454, 'class': 539, 'module': 102}","{'module': 12, 'class': 64, 'def': 646}" +micropython-latest-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-latest-samd-SEEED_WIO_TERMINAL-merged,99.99.99,samd,0.87,0.88,0.61,"{'def': 1546, 'class': 624, 'module': 114}","{'module': 15, 'class': 76, 'def': 608}" micropython-latest-stm32-merged,..\repos\micropython-stubs\stubs\micropython-latest-stm32-merged,99.99.99,stm32,0.92,0.89,0.66,"{'def': 2066, 'class': 559, 'module': 118}","{'module': 10, 'class': 62, 'def': 708}" -micropython-latest-stm32-PYBV11-merged,..\repos\micropython-stubs\stubs\micropython-latest-stm32-PYBV11-merged,99.99.99,stm32,0.92,0.89,0.66,"{'def': 2066, 'class': 559, 'module': 118}","{'module': 10, 'class': 62, 'def': 708}" +micropython-latest-stm32-PYBV11-merged,..\repos\micropython-stubs\stubs\micropython-latest-stm32-PYBV11-merged,99.99.99,stm32,0.9,0.89,0.67,"{'def': 2190, 'class': 630, 'module': 124}","{'module': 13, 'class': 72, 'def': 726}" +micropython-latest-webassembly-GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-latest-webassembly-GENERIC-merged,99.99.99,webassembly,0.97,0.77,0.64,"{'def': 688, 'class': 70, 'module': 80}","{'module': 2, 'class': 16, 'def': 246}" micropython-pico-go,..\repos\micropython-stubs\stubs\micropython-pico-go,pico,go,0.81,0.95,0.95,"{'def': 1657, 'class': 404, 'module': 58}","{'module': 11, 'class': 20, 'def': 85}" micropython-ulab,..\repos\micropython-stubs\stubs\micropython-ulab,ulab,unknown,0.5,0.5,0.63,"{'def': 222, 'class': 2, 'module': 18}","{'module': 9, 'class': 1, 'def': 83}" micropython-v1_10-esp32,..\repos\micropython-stubs\stubs\micropython-v1_10-esp32,1.10,esp32,0.52,0.53,0.11,"{'def': 1514, 'class': 229, 'module': 128}","{'module': 62, 'class': 107, 'def': 1350}" @@ -36,7 +39,6 @@ micropython-v1_15-esp32,..\repos\micropython-stubs\stubs\micropython-v1_15-esp32 micropython-v1_15-esp8266,..\repos\micropython-stubs\stubs\micropython-v1_15-esp8266,1.15,esp8266,0.51,0.6,0.11,"{'def': 2015, 'class': 355, 'module': 149}","{'module': 73, 'class': 141, 'def': 1793}" micropython-v1_15-frozen,..\repos\micropython-stubs\stubs\micropython-v1_15-frozen,1.15,frozen,0.16,0.17,0.31,"{'def': 2146, 'class': 328, 'module': 242}","{'module': 204, 'class': 273, 'def': 1489}" micropython-v1_15-pyboard,..\repos\micropython-stubs\stubs\micropython-v1_15-pyboard,1.15,pyboard,0.52,0.77,0.11,"{'def': 2448, 'class': 684, 'module': 116}","{'module': 56, 'class': 155, 'def': 2184}" -micropython-v1_16-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_16-docstubs,1.16,docstubs,1.0,1.0,1.0,"{'def': 0, 'class': 0, 'module': 0}","{'module': 0, 'class': 0, 'def': 0}" micropython-v1_16-esp32,..\repos\micropython-stubs\stubs\micropython-v1_16-esp32,1.16,esp32,0.52,0.6,0.11,"{'def': 2094, 'class': 379, 'module': 150}","{'module': 72, 'class': 150, 'def': 1854}" micropython-v1_16-esp8266,..\repos\micropython-stubs\stubs\micropython-v1_16-esp8266,1.16,esp8266,0.51,0.61,0.11,"{'def': 2021, 'class': 360, 'module': 149}","{'module': 73, 'class': 141, 'def': 1799}" micropython-v1_16-frozen,..\repos\micropython-stubs\stubs\micropython-v1_16-frozen,1.16,frozen,0.17,0.17,0.32,"{'def': 3692, 'class': 580, 'module': 440}","{'module': 365, 'class': 479, 'def': 2502}" @@ -63,77 +65,96 @@ micropython-v1_18-stm32,..\repos\micropython-stubs\stubs\micropython-v1_18-stm32 micropython-v1_18-stm32-merged,..\repos\micropython-stubs\stubs\micropython-v1_18-stm32-merged,1.18,stm32,0.89,0.94,0.65,"{'def': 2028, 'class': 599, 'module': 114}","{'module': 13, 'class': 38, 'def': 712}" micropython-v1_19-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_19-docstubs,1.19,docstubs,1.0,0.95,0.97,"{'def': 813, 'class': 109, 'module': 50}","{'module': 0, 'class': 5, 'def': 25}" micropython-v1_19-frozen,..\repos\micropython-stubs\stubs\micropython-v1_19-frozen,1.19,frozen,0.31,0.35,0.45,"{'def': 4146, 'class': 721, 'module': 473}","{'module': 325, 'class': 468, 'def': 2299}" -micropython-v1_19_1-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_19_1-docstubs,1.19.1,docstubs,1.0,0.95,0.97,"{'def': 813, 'class': 109, 'module': 50}","{'module': 0, 'class': 5, 'def': 25}" -micropython-v1_19_1-esp32,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32,1.19.1,esp32,0.51,0.21,0.1,"{'def': 1890, 'class': 260, 'module': 152}","{'module': 74, 'class': 205, 'def': 1694}" -micropython-v1_19_1-esp32-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-merged,1.19.1,esp32,0.84,0.73,0.58,"{'def': 1934, 'class': 305, 'module': 150}","{'module': 24, 'class': 82, 'def': 806}" -micropython-v1_19_1-esp32-s3,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-s3,1.19.1,esp32,1.0,0.33,0.11,"{'def': 1027, 'class': 167, 'module': 75}","{'module': 0, 'class': 112, 'def': 919}" -micropython-v1_19_1-esp32-S3-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-S3-merged,1.19.1,esp32,1.0,0.79,0.59,"{'def': 974, 'class': 178, 'module': 74}","{'module': 0, 'class': 37, 'def': 396}" -micropython-v1_19_1-esp32-UM_TINYPICO,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-UM_TINYPICO,1.19.1,esp32,0.91,0.87,0.12,"{'def': 1021, 'class': 213, 'module': 79}","{'module': 7, 'class': 28, 'def': 898}" -micropython-v1_19_1-esp32-UM_TINYPICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-UM_TINYPICO-merged,1.19.1,esp32,0.91,0.88,0.56,"{'def': 1049, 'class': 232, 'module': 79}","{'module': 7, 'class': 28, 'def': 464}" +micropython-v1_19_1-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_19_1-docstubs,1.19.1,docstubs,1.0,0.96,0.97,"{'def': 813, 'class': 113, 'module': 50}","{'module': 0, 'class': 5, 'def': 25}" +micropython-v1_19_1-esp32-GENERIC,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-GENERIC,1.19.1,esp32,0.51,0.21,0.1,"{'def': 1890, 'class': 260, 'module': 152}","{'module': 74, 'class': 205, 'def': 1694}" +micropython-v1_19_1-esp32-GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-GENERIC-merged,1.19.1,esp32,0.84,0.74,0.58,"{'def': 1934, 'class': 313, 'module': 150}","{'module': 24, 'class': 82, 'def': 806}" +micropython-v1_19_1-esp32-GENERIC_S3,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-GENERIC_S3,1.19.1,esp32,0.52,0.21,0.1,"{'def': 1977, 'class': 268, 'module': 149}","{'module': 71, 'class': 213, 'def': 1771}" +micropython-v1_19_1-esp32-GENERIC_S3-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-GENERIC_S3-merged,1.19.1,esp32,0.86,0.76,0.59,"{'def': 1948, 'class': 311, 'module': 148}","{'module': 20, 'class': 74, 'def': 792}" +micropython-v1_19_1-esp32-GENERIC_SPIRAM,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-GENERIC_SPIRAM,1.19.1,esp32,0.51,0.21,0.1,"{'def': 1890, 'class': 260, 'module': 152}","{'module': 74, 'class': 205, 'def': 1694}" +micropython-v1_19_1-esp32-GENERIC_SPIRAM-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-GENERIC_SPIRAM-merged,1.19.1,esp32,0.84,0.74,0.58,"{'def': 1934, 'class': 313, 'module': 150}","{'module': 24, 'class': 82, 'def': 806}" +micropython-v1_19_1-esp32-UM_TINYPICO,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-UM_TINYPICO,1.19.1,esp32,0.52,0.61,0.11,"{'def': 1862, 'class': 302, 'module': 147}","{'module': 71, 'class': 117, 'def': 1654}" +micropython-v1_19_1-esp32-UM_TINYPICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp32-UM_TINYPICO-merged,1.19.1,esp32,0.84,0.85,0.6,"{'def': 1866, 'class': 328, 'module': 146}","{'module': 24, 'class': 50, 'def': 744}" micropython-v1_19_1-esp8266,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp8266,1.19.1,esp8266,0.51,0.09,0.1,"{'def': 1368, 'class': 148, 'module': 118}","{'module': 58, 'class': 134, 'def': 1236}" -micropython-v1_19_1-esp8266-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp8266-merged,1.19.1,esp8266,0.89,0.78,0.59,"{'def': 1412, 'class': 178, 'module': 118}","{'module': 13, 'class': 40, 'def': 572}" -micropython-v1_19_1-frozen,..\repos\micropython-stubs\stubs\micropython-v1_19_1-frozen,1.19.1,frozen,0.31,0.35,0.45,"{'def': 4146, 'class': 721, 'module': 473}","{'module': 325, 'class': 468, 'def': 2299}" +micropython-v1_19_1-esp8266-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-esp8266-merged,1.19.1,esp8266,0.89,0.78,0.59,"{'def': 1412, 'class': 186, 'module': 118}","{'module': 13, 'class': 40, 'def': 572}" +micropython-v1_19_1-frozen,..\repos\micropython-stubs\stubs\micropython-v1_19_1-frozen,1.19.1,frozen,0.23,0.25,0.41,"{'def': 8120, 'class': 1319, 'module': 935}","{'module': 720, 'class': 991, 'def': 4753}" micropython-v1_19_1-rp2,..\repos\micropython-stubs\stubs\micropython-v1_19_1-rp2,1.19.1,rp2,0.52,0.32,0.12,"{'def': 1992, 'class': 373, 'module': 142}","{'module': 68, 'class': 254, 'def': 1758}" -micropython-v1_19_1-rp2-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-rp2-merged,1.19.1,rp2,0.84,0.65,0.54,"{'def': 1974, 'class': 395, 'module': 140}","{'module': 22, 'class': 137, 'def': 902}" +micropython-v1_19_1-rp2-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-rp2-merged,1.19.1,rp2,0.84,0.67,0.56,"{'def': 1974, 'class': 403, 'module': 140}","{'module': 22, 'class': 131, 'def': 874}" micropython-v1_19_1-rp2_simple,..\repos\micropython-stubs\stubs\micropython-v1_19_1-rp2_simple,1.19.1,rp2_simple,0.53,0.26,0.1,"{'def': 1357, 'class': 195, 'module': 105}","{'module': 49, 'class': 144, 'def': 1222}" micropython-v1_19_1-stm32,..\repos\micropython-stubs\stubs\micropython-v1_19_1-stm32,1.19.1,stm32,0.51,0.65,0.1,"{'def': 2087, 'class': 549, 'module': 115}","{'module': 56, 'class': 194, 'def': 1888}" -micropython-v1_19_1-stm32-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-stm32-merged,1.19.1,stm32,0.89,0.9,0.64,"{'def': 2088, 'class': 573, 'module': 114}","{'module': 12, 'class': 60, 'def': 754}" -micropython-v1_20_0-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_20_0-docstubs,1.20.0,docstubs,1.0,0.95,0.97,"{'def': 823, 'class': 108, 'module': 50}","{'module': 0, 'class': 5, 'def': 24}" -micropython-v1_20_0-esp32,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32,1.20.0,esp32,0.52,0.21,0.1,"{'def': 1908, 'class': 262, 'module': 158}","{'module': 76, 'class': 206, 'def': 1710}" -micropython-v1_20_0-esp32-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-merged,1.20.0,esp32,0.84,0.73,0.61,"{'def': 1966, 'class': 306, 'module': 158}","{'module': 25, 'class': 84, 'def': 764}" -micropython-v1_20_0-esp32-OTA,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-OTA,1.20.0,esp32,0.52,0.21,0.1,"{'def': 1908, 'class': 262, 'module': 158}","{'module': 76, 'class': 206, 'def': 1710}" -micropython-v1_20_0-esp32-OTA-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-OTA-merged,1.20.0,esp32,0.84,0.73,0.61,"{'def': 1966, 'class': 306, 'module': 158}","{'module': 25, 'class': 84, 'def': 764}" -micropython-v1_20_0-esp32-S3,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-S3,1.20.0,esp32,0.52,0.22,0.1,"{'def': 1896, 'class': 263, 'module': 156}","{'module': 75, 'class': 204, 'def': 1700}" -micropython-v1_20_0-esp32-S3-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-S3-merged,1.20.0,esp32,0.85,0.72,0.61,"{'def': 1944, 'class': 303, 'module': 156}","{'module': 24, 'class': 84, 'def': 766}" -micropython-v1_20_0-frozen,..\repos\micropython-stubs\stubs\micropython-v1_20_0-frozen,1.20.0,frozen,0.15,0.15,0.32,"{'def': 18676, 'class': 2959, 'module': 2019}","{'module': 1711, 'class': 2517, 'def': 12774}" -micropython-v1_20_0-rp2,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2,1.20.0,rp2,0.54,0.52,0.11,"{'def': 1518, 'class': 339, 'module': 116}","{'module': 53, 'class': 164, 'def': 1354}" -micropython-v1_20_0-rp2-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-merged,1.20.0,rp2,0.91,0.81,0.57,"{'def': 1546, 'class': 363, 'module': 116}","{'module': 11, 'class': 69, 'def': 660}" +micropython-v1_19_1-stm32-merged,..\repos\micropython-stubs\stubs\micropython-v1_19_1-stm32-merged,1.19.1,stm32,0.89,0.9,0.64,"{'def': 2088, 'class': 581, 'module': 114}","{'module': 12, 'class': 60, 'def': 754}" +micropython-v1_20_0-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_20_0-docstubs,1.20.0,docstubs,1.0,0.96,0.97,"{'def': 823, 'class': 112, 'module': 50}","{'module': 0, 'class': 5, 'def': 24}" +micropython-v1_20_0-esp32,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32,1.20.0,esp32,0.68,0.49,0.36,"{'def': 3874, 'class': 568, 'module': 316}","{'module': 101, 'class': 290, 'def': 2474}" +micropython-v1_20_0-esp32-GENERIC,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-GENERIC,1.20.0,esp32,0.52,0.21,0.1,"{'def': 1908, 'class': 262, 'module': 158}","{'module': 76, 'class': 206, 'def': 1710}" +micropython-v1_20_0-esp32-GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-GENERIC-merged,1.20.0,esp32,0.84,0.73,0.61,"{'def': 1966, 'class': 314, 'module': 158}","{'module': 25, 'class': 84, 'def': 764}" +micropython-v1_20_0-esp32-GENERIC_OTA,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-GENERIC_OTA,1.20.0,esp32,0.52,0.21,0.1,"{'def': 1908, 'class': 262, 'module': 158}","{'module': 76, 'class': 206, 'def': 1710}" +micropython-v1_20_0-esp32-GENERIC_OTA-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-GENERIC_OTA-merged,1.20.0,esp32,0.84,0.73,0.61,"{'def': 1966, 'class': 314, 'module': 158}","{'module': 25, 'class': 84, 'def': 764}" +micropython-v1_20_0-esp32-GENERIC_S3,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-GENERIC_S3,1.20.0,esp32,0.52,0.22,0.1,"{'def': 1896, 'class': 263, 'module': 156}","{'module': 75, 'class': 204, 'def': 1700}" +micropython-v1_20_0-esp32-GENERIC_S3-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-esp32-GENERIC_S3-merged,1.20.0,esp32,0.85,0.73,0.61,"{'def': 1944, 'class': 311, 'module': 156}","{'module': 24, 'class': 84, 'def': 766}" +micropython-v1_20_0-frozen,..\repos\micropython-stubs\stubs\micropython-v1_20_0-frozen,1.20.0,frozen,0.15,0.16,0.32,"{'def': 18676, 'class': 2964, 'module': 2019}","{'module': 1711, 'class': 2475, 'def': 12639}" micropython-v1_20_0-rp2-PICO,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PICO,1.20.0,rp2,0.54,0.52,0.11,"{'def': 1518, 'class': 339, 'module': 116}","{'module': 53, 'class': 164, 'def': 1354}" -micropython-v1_20_0-rp2-PICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PICO-merged,1.20.0,rp2,0.91,0.81,0.57,"{'def': 1546, 'class': 363, 'module': 116}","{'module': 11, 'class': 69, 'def': 660}" +micropython-v1_20_0-rp2-PICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PICO-merged,1.20.0,rp2,0.91,0.83,0.59,"{'def': 1546, 'class': 371, 'module': 116}","{'module': 11, 'class': 63, 'def': 630}" micropython-v1_20_0-rp2-PICO_W,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PICO_W,1.20.0,rp2,0.54,0.51,0.1,"{'def': 1748, 'class': 365, 'module': 138}","{'module': 64, 'class': 178, 'def': 1570}" -micropython-v1_20_0-rp2-PICO_W-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PICO_W-merged,1.20.0,rp2,0.88,0.8,0.56,"{'def': 1776, 'class': 389, 'module': 138}","{'module': 17, 'class': 77, 'def': 780}" +micropython-v1_20_0-rp2-PICO_W-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PICO_W-merged,1.20.0,rp2,0.88,0.82,0.58,"{'def': 1776, 'class': 397, 'module': 138}","{'module': 17, 'class': 71, 'def': 750}" micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB,1.20.0,rp2,0.54,0.42,0.11,"{'def': 1518, 'class': 285, 'module': 116}","{'module': 53, 'class': 164, 'def': 1354}" -micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB-merged,1.20.0,rp2,0.91,0.78,0.57,"{'def': 1546, 'class': 309, 'module': 116}","{'module': 11, 'class': 69, 'def': 660}" +micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB-merged,1.20.0,rp2,0.91,0.8,0.59,"{'def': 1546, 'class': 317, 'module': 116}","{'module': 11, 'class': 63, 'def': 630}" micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS,1.20.0,samd,0.52,0.61,0.11,"{'def': 1426, 'class': 377, 'module': 102}","{'module': 49, 'class': 148, 'def': 1276}" -micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged,1.20.0,samd,0.88,0.85,0.56,"{'def': 1454, 'class': 401, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" +micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged,1.20.0,samd,0.88,0.85,0.56,"{'def': 1454, 'class': 409, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS,1.20.0,samd,0.52,0.58,0.11,"{'def': 1426, 'class': 351, 'module': 102}","{'module': 49, 'class': 148, 'def': 1276}" -micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged,1.20.0,samd,0.88,0.84,0.56,"{'def': 1454, 'class': 375, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" +micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged,1.20.0,samd,0.88,0.84,0.56,"{'def': 1454, 'class': 383, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" micropython-v1_20_0-samd-MINISAM_M4,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-MINISAM_M4,1.20.0,samd,0.52,0.56,0.11,"{'def': 1426, 'class': 333, 'module': 102}","{'module': 49, 'class': 148, 'def': 1276}" -micropython-v1_20_0-samd-MINISAM_M4-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-MINISAM_M4-merged,1.20.0,samd,0.88,0.83,0.56,"{'def': 1454, 'class': 357, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" +micropython-v1_20_0-samd-MINISAM_M4-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-MINISAM_M4-merged,1.20.0,samd,0.88,0.84,0.56,"{'def': 1454, 'class': 365, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" micropython-v1_20_0-samd-SEEED_WIO_TERMINAL,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-SEEED_WIO_TERMINAL,1.20.0,samd,0.52,0.71,0.11,"{'def': 1426, 'class': 519, 'module': 102}","{'module': 49, 'class': 148, 'def': 1276}" -micropython-v1_20_0-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-SEEED_WIO_TERMINAL-merged,1.20.0,samd,0.88,0.89,0.56,"{'def': 1454, 'class': 543, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" +micropython-v1_20_0-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-samd-SEEED_WIO_TERMINAL-merged,1.20.0,samd,0.88,0.89,0.56,"{'def': 1454, 'class': 551, 'module': 102}","{'module': 12, 'class': 60, 'def': 646}" micropython-v1_20_0-stm32,..\repos\micropython-stubs\stubs\micropython-v1_20_0-stm32,1.20.0,stm32,0.53,0.66,0.09,"{'def': 2034, 'class': 539, 'module': 118}","{'module': 56, 'class': 184, 'def': 1844}" -micropython-v1_20_0-stm32-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-stm32-merged,1.20.0,stm32,0.9,0.9,0.65,"{'def': 2066, 'class': 563, 'module': 118}","{'module': 12, 'class': 58, 'def': 720}" micropython-v1_20_0-stm32-PYBV11,..\repos\micropython-stubs\stubs\micropython-v1_20_0-stm32-PYBV11,1.20.0,stm32,0.53,0.66,0.09,"{'def': 2034, 'class': 539, 'module': 118}","{'module': 56, 'class': 184, 'def': 1844}" -micropython-v1_20_0-stm32-PYBV11-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-stm32-PYBV11-merged,1.20.0,stm32,0.9,0.9,0.65,"{'def': 2066, 'class': 563, 'module': 118}","{'module': 12, 'class': 58, 'def': 720}" -micropython-v1_21_0-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_21_0-docstubs,1.21.0,docstubs,1.0,0.96,0.97,"{'def': 915, 'class': 124, 'module': 55}","{'module': 0, 'class': 5, 'def': 24}" -micropython-v1_21_0-esp32,..\repos\micropython-stubs\stubs\micropython-v1_21_0-esp32,1.21.0,esp32,0.51,0.29,0.1,"{'def': 2220, 'class': 339, 'module': 179}","{'module': 87, 'class': 242, 'def': 1988}" +micropython-v1_20_0-stm32-PYBV11-merged,..\repos\micropython-stubs\stubs\micropython-v1_20_0-stm32-PYBV11-merged,1.20.0,stm32,0.9,0.9,0.65,"{'def': 2066, 'class': 571, 'module': 118}","{'module': 12, 'class': 58, 'def': 720}" +micropython-v1_21_0-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_21_0-docstubs,1.21.0,docstubs,1.0,0.96,0.97,"{'def': 915, 'class': 128, 'module': 55}","{'module': 0, 'class': 5, 'def': 24}" micropython-v1_21_0-esp32-ESP32_GENERIC,..\repos\micropython-stubs\stubs\micropython-v1_21_0-esp32-ESP32_GENERIC,1.21.0,esp32,0.51,0.29,0.1,"{'def': 2220, 'class': 339, 'module': 179}","{'module': 87, 'class': 242, 'def': 1988}" -micropython-v1_21_0-esp32-ESP32_GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-esp32-ESP32_GENERIC-merged,1.21.0,esp32,0.83,0.7,0.58,"{'def': 2236, 'class': 377, 'module': 174}","{'module': 30, 'class': 112, 'def': 950}" -micropython-v1_21_0-esp32-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-esp32-merged,1.21.0,esp32,0.83,0.7,0.58,"{'def': 2236, 'class': 377, 'module': 174}","{'module': 30, 'class': 112, 'def': 950}" -micropython-v1_21_0-frozen,..\repos\micropython-stubs\stubs\micropython-v1_21_0-frozen,1.21.0,frozen,0.23,0.15,0.36,"{'def': 19937, 'class': 2989, 'module': 2338}","{'module': 1795, 'class': 2554, 'def': 12856}" +micropython-v1_21_0-esp32-ESP32_GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-esp32-ESP32_GENERIC-merged,1.21.0,esp32,0.84,0.71,0.59,"{'def': 2248, 'class': 385, 'module': 174}","{'module': 28, 'class': 110, 'def': 918}" +micropython-v1_21_0-frozen,..\repos\micropython-stubs\stubs\micropython-v1_21_0-frozen,1.21.0,frozen,0.23,0.17,0.36,"{'def': 19937, 'class': 2996, 'module': 2338}","{'module': 1795, 'class': 2494, 'def': 12679}" micropython-v1_21_0-rp2-RPI_PICO,..\repos\micropython-stubs\stubs\micropython-v1_21_0-rp2-RPI_PICO,1.21.0,rp2,0.54,0.53,0.11,"{'def': 1648, 'class': 402, 'module': 128}","{'module': 59, 'class': 188, 'def': 1464}" -micropython-v1_21_0-rp2-RPI_PICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-rp2-RPI_PICO-merged,1.21.0,rp2,0.88,0.78,0.55,"{'def': 1676, 'class': 424, 'module': 128}","{'module': 16, 'class': 95, 'def': 754}" +micropython-v1_21_0-rp2-RPI_PICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-rp2-RPI_PICO-merged,1.21.0,rp2,0.89,0.82,0.62,"{'def': 1688, 'class': 432, 'module': 128}","{'module': 14, 'class': 79, 'def': 644}" micropython-v1_21_0-rp2-RPI_PICO_W,..\repos\micropython-stubs\stubs\micropython-v1_21_0-rp2-RPI_PICO_W,1.21.0,rp2,0.53,0.52,0.12,"{'def': 2248, 'class': 602, 'module': 176}","{'module': 83, 'class': 286, 'def': 1978}" -micropython-v1_21_0-rp2-RPI_PICO_W-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-rp2-RPI_PICO_W-merged,1.21.0,rp2,0.82,0.72,0.52,"{'def': 2276, 'class': 628, 'module': 174}","{'module': 32, 'class': 175, 'def': 1088}" -micropython-v1_21_0-samd-SEEED_WIO_TERMINAL,..\repos\micropython-stubs\stubs\micropython-v1_21_0-samd-SEEED_WIO_TERMINAL,1.21.0,samd,0.52,0.72,0.11,"{'def': 1506, 'class': 594, 'module': 114}","{'module': 55, 'class': 168, 'def': 1340}" -micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged,1.21.0,samd,0.85,0.86,0.55,"{'def': 1534, 'class': 616, 'module': 114}","{'module': 17, 'class': 86, 'def': 688}" +micropython-v1_21_0-rp2-RPI_PICO_W-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-rp2-RPI_PICO_W-merged,1.21.0,rp2,0.83,0.75,0.57,"{'def': 2288, 'class': 636, 'module': 174}","{'module': 30, 'class': 159, 'def': 978}" +micropython-v1_21_0-samd-SEEED_WIO_TERMINAL,..\repos\micropython-stubs\stubs\micropython-v1_21_0-samd-SEEED_WIO_TERMINAL,1.21.0,samd,0.52,0.71,0.11,"{'def': 1534, 'class': 596, 'module': 116}","{'module': 56, 'class': 170, 'def': 1366}" +micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged,1.21.0,samd,0.86,0.88,0.6,"{'def': 1574, 'class': 626, 'module': 116}","{'module': 16, 'class': 78, 'def': 634}" micropython-v1_21_0-stm32-PYBV11,..\repos\micropython-stubs\stubs\micropython-v1_21_0-stm32-PYBV11,1.21.0,stm32,0.52,0.66,0.1,"{'def': 2146, 'class': 600, 'module': 124}","{'module': 60, 'class': 206, 'def': 1938}" -micropython-v1_21_0-stm32-PYBV11-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-stm32-PYBV11-merged,1.21.0,stm32,0.88,0.87,0.63,"{'def': 2178, 'class': 622, 'module': 124}","{'module': 15, 'class': 82, 'def': 806}" +micropython-v1_21_0-stm32-PYBV11-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-stm32-PYBV11-merged,1.21.0,stm32,0.9,0.89,0.67,"{'def': 2190, 'class': 630, 'module': 124}","{'module': 13, 'class': 72, 'def': 726}" +micropython-v1_21_0-webassembly-GENERIC,..\repos\micropython-stubs\stubs\micropython-v1_21_0-webassembly-GENERIC,1.21.0,webassembly,0.51,0.0,0.07,"{'def': 664, 'class': 44, 'module': 80}","{'module': 39, 'class': 44, 'def': 620}" +micropython-v1_21_0-webassembly-GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-v1_21_0-webassembly-GENERIC-merged,1.21.0,webassembly,0.97,0.77,0.64,"{'def': 688, 'class': 70, 'module': 80}","{'module': 2, 'class': 16, 'def': 246}" +micropython-v1_22_0-docstubs,..\repos\micropython-stubs\stubs\micropython-v1_22_0-docstubs,1.22.0,docstubs,1.0,0.96,0.97,"{'def': 919, 'class': 120, 'module': 55}","{'module': 0, 'class': 5, 'def': 24}" +micropython-v1_22_0-esp32-ESP32_GENERIC,..\repos\micropython-stubs\stubs\micropython-v1_22_0-esp32-ESP32_GENERIC,1.22.0,esp32,0.52,0.3,0.11,"{'def': 2242, 'class': 353, 'module': 178}","{'module': 85, 'class': 248, 'def': 2002}" +micropython-v1_22_0-esp32-ESP32_GENERIC-merged,..\repos\micropython-stubs\stubs\micropython-v1_22_0-esp32-ESP32_GENERIC-merged,1.22.0,esp32,0.84,0.72,0.59,"{'def': 2274, 'class': 397, 'module': 174}","{'module': 28, 'class': 112, 'def': 922}" +micropython-v1_22_0-esp32-ESP32_GENERIC_S3,..\repos\micropython-stubs\stubs\micropython-v1_22_0-esp32-ESP32_GENERIC_S3,1.22.0,esp32,0.52,0.3,0.11,"{'def': 2232, 'class': 349, 'module': 178}","{'module': 85, 'class': 244, 'def': 1996}" +micropython-v1_22_0-esp32-ESP32_GENERIC_S3-merged,..\repos\micropython-stubs\stubs\micropython-v1_22_0-esp32-ESP32_GENERIC_S3-merged,1.22.0,esp32,0.84,0.73,0.59,"{'def': 2264, 'class': 393, 'module': 174}","{'module': 28, 'class': 108, 'def': 918}" +micropython-v1_22_0-esp32-Generic_ESP32S3_module,..\repos\micropython-stubs\stubs\micropython-v1_22_0-esp32-Generic_ESP32S3_module,1.22.0,esp32,0.52,0.3,0.11,"{'def': 2232, 'class': 349, 'module': 178}","{'module': 85, 'class': 244, 'def': 1996}" +micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT,..\repos\micropython-stubs\stubs\micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT,1.22.0,rp2,0.53,0.52,0.12,"{'def': 2292, 'class': 616, 'module': 180}","{'module': 85, 'class': 298, 'def': 2010}" +micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT-merged,..\repos\micropython-stubs\stubs\micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT-merged,1.22.0,rp2,0.82,0.75,0.58,"{'def': 2312, 'class': 646, 'module': 176}","{'module': 31, 'class': 161, 'def': 976}" +micropython-v1_22_0-rp2-RPI_PICO,..\repos\micropython-stubs\stubs\micropython-v1_22_0-rp2-RPI_PICO,1.22.0,rp2,0.54,0.53,0.12,"{'def': 1612, 'class': 408, 'module': 130}","{'module': 60, 'class': 192, 'def': 1424}" +micropython-v1_22_0-rp2-RPI_PICO-merged,..\repos\micropython-stubs\stubs\micropython-v1_22_0-rp2-RPI_PICO-merged,1.22.0,rp2,0.88,0.82,0.62,"{'def': 1652, 'class': 438, 'module': 130}","{'module': 15, 'class': 79, 'def': 632}" +micropython-v1_22_0-rp2-RPI_PICO_W,..\repos\micropython-stubs\stubs\micropython-v1_22_0-rp2-RPI_PICO_W,1.22.0,rp2,0.53,0.52,0.12,"{'def': 2228, 'class': 590, 'module': 180}","{'module': 85, 'class': 284, 'def': 1960}" +micropython-v1_22_0-rp2-RPI_PICO_W-merged,..\repos\micropython-stubs\stubs\micropython-v1_22_0-rp2-RPI_PICO_W-merged,1.22.0,rp2,0.82,0.76,0.58,"{'def': 2248, 'class': 620, 'module': 176}","{'module': 31, 'class': 151, 'def': 954}" +micropython-v1_22_0-samd-SEEED_WIO_TERMINAL,..\repos\micropython-stubs\stubs\micropython-v1_22_0-samd-SEEED_WIO_TERMINAL,1.22.0,samd,0.52,0.72,0.11,"{'def': 1488, 'class': 604, 'module': 116}","{'module': 56, 'class': 172, 'def': 1318}" +micropython-v1_22_0-samd-SEEED_WIO_TERMINAL-merged,..\repos\micropython-stubs\stubs\micropython-v1_22_0-samd-SEEED_WIO_TERMINAL-merged,1.22.0,samd,0.86,0.88,0.6,"{'def': 1528, 'class': 634, 'module': 116}","{'module': 16, 'class': 76, 'def': 614}" micropython-v1_9_3-esp8266,..\repos\micropython-stubs\stubs\micropython-v1_9_3-esp8266,1.9.3,esp8266,0.51,0.58,0.01,"{'def': 1112, 'class': 182, 'module': 117}","{'module': 57, 'class': 76, 'def': 1100}" micropython-v1_9_3-frozen,..\repos\micropython-stubs\stubs\micropython-v1_9_3-frozen,1.9.3,frozen,0.05,0.0,0.16,"{'def': 363, 'class': 40, 'module': 40}","{'module': 38, 'class': 40, 'def': 306}" micropython-v1_9_4-esp8266,..\repos\micropython-stubs\stubs\micropython-v1_9_4-esp8266,1.9.4,esp8266,0.52,0.5,0.02,"{'def': 642, 'class': 104, 'module': 119}","{'module': 57, 'class': 52, 'def': 630}" micropython-v1_9_4-frozen,..\repos\micropython-stubs\stubs\micropython-v1_9_4-frozen,1.9.4,frozen,0.05,0.0,0.17,"{'def': 571, 'class': 82, 'module': 74}","{'module': 70, 'class': 82, 'def': 474}" -micropython-latest-esp32-stubs,..\repos\micropython-stubs\publish\micropython-latest-esp32-stubs,99.99.99,esp32,0.61,0.59,0.59,"{'def': 1148, 'class': 145, 'module': 94}","{'module': 37, 'class': 59, 'def': 475}" -micropython-latest-rp2-pimoroni_picolipo_16mb-stubs,..\repos\micropython-stubs\publish\micropython-latest-rp2-pimoroni_picolipo_16mb-stubs,99.99.99,rp2,0.7,0.55,0.55,"{'def': 844, 'class': 107, 'module': 66}","{'module': 20, 'class': 48, 'def': 377}" -micropython-latest-rp2-stubs,..\repos\micropython-stubs\publish\micropython-latest-rp2-stubs,99.99.99,rp2,0.7,0.55,0.55,"{'def': 844, 'class': 107, 'module': 66}","{'module': 20, 'class': 48, 'def': 377}" -micropython-latest-samd-adafruit_feather_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-adafruit_feather_m4_express-stubs,99.99.99,samd,0.68,0.58,0.54,"{'def': 798, 'class': 99, 'module': 59}","{'module': 19, 'class': 42, 'def': 368}" -micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs,99.99.99,samd,0.68,0.58,0.54,"{'def': 798, 'class': 99, 'module': 59}","{'module': 19, 'class': 42, 'def': 368}" -micropython-latest-samd-minisam_m4-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-minisam_m4-stubs,99.99.99,samd,0.68,0.58,0.54,"{'def': 798, 'class': 99, 'module': 59}","{'module': 19, 'class': 42, 'def': 368}" -micropython-latest-samd-seeed_wio_terminal-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-seeed_wio_terminal-stubs,99.99.99,samd,0.68,0.58,0.54,"{'def': 798, 'class': 99, 'module': 59}","{'module': 19, 'class': 42, 'def': 368}" -micropython-latest-stm32-pybv11-stubs,..\repos\micropython-stubs\publish\micropython-latest-stm32-pybv11-stubs,99.99.99,stm32,0.74,0.66,0.64,"{'def': 1104, 'class': 119, 'module': 66}","{'module': 17, 'class': 41, 'def': 400}" -micropython-latest-stm32-stubs,..\repos\micropython-stubs\publish\micropython-latest-stm32-stubs,99.99.99,stm32,0.74,0.66,0.64,"{'def': 1104, 'class': 119, 'module': 66}","{'module': 17, 'class': 41, 'def': 400}" -micropython-stdlib-stubs,..\repos\micropython-stubs\publish\micropython-stdlib-stubs,1.0,stdlib,1.0,1.0,1.0,"{'def': 3625, 'class': 658, 'module': 63}","{'module': 0, 'class': 0, 'def': 0}" +micropython-latest-esp32-esp32_generic-stubs,..\repos\micropython-stubs\publish\micropython-latest-esp32-esp32_generic-stubs,99.99.99,esp32,0.68,0.6,0.6,"{'def': 1096, 'class': 138, 'module': 87}","{'module': 28, 'class': 55, 'def': 435}" +micropython-latest-esp32-stubs,..\repos\micropython-stubs\publish\micropython-latest-esp32-stubs,99.99.99,esp32,0.68,0.6,0.6,"{'def': 1096, 'class': 138, 'module': 87}","{'module': 28, 'class': 55, 'def': 435}" +micropython-latest-rp2-pimoroni_picolipo_16mb-stubs,..\repos\micropython-stubs\publish\micropython-latest-rp2-pimoroni_picolipo_16mb-stubs,99.99.99,rp2,0.81,0.58,0.59,"{'def': 745, 'class': 88, 'module': 58}","{'module': 11, 'class': 37, 'def': 306}" +micropython-latest-rp2-rpi_pico-stubs,..\repos\micropython-stubs\publish\micropython-latest-rp2-rpi_pico-stubs,99.99.99,rp2,0.78,0.62,0.63,"{'def': 816, 'class': 105, 'module': 64}","{'module': 14, 'class': 40, 'def': 298}" +micropython-latest-rp2-rpi_pico_w-stubs,..\repos\micropython-stubs\publish\micropython-latest-rp2-rpi_pico_w-stubs,99.99.99,rp2,0.64,0.54,0.62,"{'def': 1142, 'class': 138, 'module': 92}","{'module': 33, 'class': 63, 'def': 435}" +micropython-latest-rp2-stubs,..\repos\micropython-stubs\publish\micropython-latest-rp2-stubs,99.99.99,rp2,0.78,0.62,0.63,"{'def': 816, 'class': 105, 'module': 64}","{'module': 14, 'class': 40, 'def': 298}" +micropython-latest-samd-adafruit_feather_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-adafruit_feather_m4_express-stubs,99.99.99,samd,0.76,0.6,0.57,"{'def': 700, 'class': 80, 'module': 51}","{'module': 12, 'class': 32, 'def': 299}" +micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs,99.99.99,samd,0.76,0.6,0.57,"{'def': 700, 'class': 80, 'module': 51}","{'module': 12, 'class': 32, 'def': 299}" +micropython-latest-samd-minisam_m4-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-minisam_m4-stubs,99.99.99,samd,0.76,0.6,0.57,"{'def': 700, 'class': 80, 'module': 51}","{'module': 12, 'class': 32, 'def': 299}" +micropython-latest-samd-seeed_wio_terminal-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-seeed_wio_terminal-stubs,99.99.99,samd,0.74,0.6,0.62,"{'def': 745, 'class': 95, 'module': 57}","{'module': 15, 'class': 38, 'def': 280}" +micropython-latest-samd-stubs,..\repos\micropython-stubs\publish\micropython-latest-samd-stubs,99.99.99,samd,0.74,0.6,0.62,"{'def': 745, 'class': 95, 'module': 57}","{'module': 15, 'class': 38, 'def': 280}" +micropython-latest-stm32-pybv11-stubs,..\repos\micropython-stubs\publish\micropython-latest-stm32-pybv11-stubs,99.99.99,stm32,0.79,0.69,0.68,"{'def': 1067, 'class': 116, 'module': 62}","{'module': 13, 'class': 36, 'def': 339}" +micropython-latest-stm32-stubs,..\repos\micropython-stubs\publish\micropython-latest-stm32-stubs,99.99.99,stm32,0.79,0.69,0.68,"{'def': 1067, 'class': 116, 'module': 62}","{'module': 13, 'class': 36, 'def': 339}" +micropython-latest-webassembly-stubs,..\repos\micropython-stubs\publish\micropython-latest-webassembly-stubs,99.99.99,webassembly,0.95,0.7,0.69,"{'def': 316, 'class': 27, 'module': 40}","{'module': 2, 'class': 8, 'def': 98}" +micropython-stdlib-stubs,..\repos\micropython-stubs\publish\micropython-stdlib-stubs,1.0,stdlib,1.0,1.0,1.0,"{'def': 3566, 'class': 649, 'module': 57}","{'module': 0, 'class': 0, 'def': 0}" micropython-v1_17-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_17-esp32-stubs,1.17,esp32,0.62,0.55,0.5,"{'def': 953, 'class': 132, 'module': 80}","{'module': 30, 'class': 60, 'def': 481}" micropython-v1_17-esp8266-stubs,..\repos\micropython-stubs\publish\micropython-v1_17-esp8266-stubs,1.17,esp8266,0.56,0.5,0.43,"{'def': 899, 'class': 113, 'module': 80}","{'module': 35, 'class': 57, 'def': 512}" micropython-v1_17-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_17-rp2-stubs,1.17,rp2,0.7,0.55,0.42,"{'def': 661, 'class': 95, 'module': 53}","{'module': 16, 'class': 43, 'def': 385}" @@ -142,28 +163,36 @@ micropython-v1_18-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_ micropython-v1_18-esp8266-stubs,..\repos\micropython-stubs\publish\micropython-v1_18-esp8266-stubs,1.18,esp8266,0.59,0.5,0.44,"{'def': 900, 'class': 113, 'module': 80}","{'module': 33, 'class': 57, 'def': 508}" micropython-v1_18-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_18-rp2-stubs,1.18,rp2,0.79,0.58,0.54,"{'def': 839, 'class': 113, 'module': 61}","{'module': 13, 'class': 47, 'def': 388}" micropython-v1_18-stm32-stubs,..\repos\micropython-stubs\publish\micropython-v1_18-stm32-stubs,1.18,stm32,0.75,0.67,0.64,"{'def': 1037, 'class': 121, 'module': 59}","{'module': 15, 'class': 40, 'def': 371}" +micropython-v1_19_1-esp32-generic_s3-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-generic_s3-stubs,1.19.1,esp32,0.64,0.62,0.58,"{'def': 1048, 'class': 136, 'module': 86}","{'module': 31, 'class': 51, 'def': 443}" +micropython-v1_19_1-esp32-generic_spiram-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-generic_spiram-stubs,1.19.1,esp32,0.65,0.66,0.59,"{'def': 1003, 'class': 129, 'module': 82}","{'module': 29, 'class': 44, 'def': 416}" micropython-v1_19_1-esp32-s3-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-s3-stubs,1.19.1,esp32,0.81,0.77,0.62,"{'def': 1007, 'class': 142, 'module': 80}","{'module': 15, 'class': 33, 'def': 381}" -micropython-v1_19_1-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-stubs,1.19.1,esp32,0.64,0.65,0.57,"{'def': 1020, 'class': 133, 'module': 81}","{'module': 29, 'class': 47, 'def': 443}" -micropython-v1_19_1-esp32-um_tinypico-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-um_tinypico-stubs,1.19.1,esp32,0.63,0.65,0.57,"{'def': 1038, 'class': 134, 'module': 83}","{'module': 31, 'class': 47, 'def': 443}" -micropython-v1_19_1-esp8266-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp8266-stubs,1.19.1,esp8266,0.71,0.72,0.58,"{'def': 739, 'class': 85, 'module': 65}","{'module': 19, 'class': 24, 'def': 311}" -micropython-v1_19_1-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-rp2-stubs,1.19.1,rp2,0.68,0.5,0.54,"{'def': 1003, 'class': 142, 'module': 73}","{'module': 23, 'class': 71, 'def': 460}" -micropython-v1_19_1-stm32-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-stm32-stubs,1.19.1,stm32,0.76,0.73,0.63,"{'def': 1066, 'class': 119, 'module': 59}","{'module': 14, 'class': 32, 'def': 391}" +micropython-v1_19_1-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-stubs,1.19.1,esp32,0.65,0.66,0.59,"{'def': 1003, 'class': 129, 'module': 82}","{'module': 29, 'class': 44, 'def': 416}" +micropython-v1_19_1-esp32-um_tinypico-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-um_tinypico-stubs,1.19.1,esp32,0.62,0.65,0.63,"{'def': 931, 'class': 110, 'module': 82}","{'module': 31, 'class': 38, 'def': 342}" +micropython-v1_19_1-esp8266-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-esp8266-stubs,1.19.1,esp8266,0.73,0.73,0.59,"{'def': 724, 'class': 85, 'module': 66}","{'module': 18, 'class': 23, 'def': 296}" +micropython-v1_19_1-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-rp2-stubs,1.19.1,rp2,0.69,0.53,0.58,"{'def': 981, 'class': 137, 'module': 74}","{'module': 23, 'class': 64, 'def': 413}" +micropython-v1_19_1-stm32-stubs,..\repos\micropython-stubs\publish\micropython-v1_19_1-stm32-stubs,1.19.1,stm32,0.77,0.75,0.66,"{'def': 1043, 'class': 114, 'module': 60}","{'module': 14, 'class': 28, 'def': 358}" +micropython-v1_20_0-esp32-generic_ota-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-esp32-generic_ota-stubs,1.20.0,esp32,0.65,0.63,0.62,"{'def': 1010, 'class': 126, 'module': 85}","{'module': 30, 'class': 46, 'def': 384}" +micropython-v1_20_0-esp32-generic_s3-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-esp32-generic_s3-stubs,1.20.0,esp32,0.65,0.64,0.62,"{'def': 980, 'class': 120, 'module': 84}","{'module': 29, 'class': 43, 'def': 369}" micropython-v1_20_0-esp32-ota-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-esp32-ota-stubs,1.20.0,esp32,0.64,0.64,0.61,"{'def': 1041, 'class': 130, 'module': 85}","{'module': 31, 'class': 47, 'def': 410}" micropython-v1_20_0-esp32-s3-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-esp32-s3-stubs,1.20.0,esp32,0.64,0.65,0.61,"{'def': 1011, 'class': 124, 'module': 84}","{'module': 30, 'class': 44, 'def': 395}" -micropython-v1_20_0-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-esp32-stubs,1.20.0,esp32,0.64,0.64,0.61,"{'def': 1041, 'class': 130, 'module': 85}","{'module': 31, 'class': 47, 'def': 410}" -micropython-v1_20_0-rp2-pico-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-pico-stubs,1.20.0,rp2,0.77,0.65,0.59,"{'def': 782, 'class': 97, 'module': 60}","{'module': 14, 'class': 34, 'def': 324}" -micropython-v1_20_0-rp2-pico_w-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-pico_w-stubs,1.20.0,rp2,0.72,0.63,0.57,"{'def': 896, 'class': 104, 'module': 71}","{'module': 20, 'class': 38, 'def': 383}" -micropython-v1_20_0-rp2-pimoroni_picolipo_16mb-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-pimoroni_picolipo_16mb-stubs,1.20.0,rp2,0.77,0.65,0.59,"{'def': 782, 'class': 97, 'module': 60}","{'module': 14, 'class': 34, 'def': 324}" -micropython-v1_20_0-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-stubs,1.20.0,rp2,0.77,0.65,0.59,"{'def': 782, 'class': 97, 'module': 60}","{'module': 14, 'class': 34, 'def': 324}" -micropython-v1_20_0-samd-adafruit_feather_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-adafruit_feather_m4_express-stubs,1.20.0,samd,0.75,0.69,0.57,"{'def': 736, 'class': 89, 'module': 53}","{'module': 13, 'class': 28, 'def': 315}" -micropython-v1_20_0-samd-adafruit_itsybitsy_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-adafruit_itsybitsy_m4_express-stubs,1.20.0,samd,0.75,0.69,0.57,"{'def': 736, 'class': 89, 'module': 53}","{'module': 13, 'class': 28, 'def': 315}" -micropython-v1_20_0-samd-minisam_m4-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-minisam_m4-stubs,1.20.0,samd,0.75,0.69,0.57,"{'def': 736, 'class': 89, 'module': 53}","{'module': 13, 'class': 28, 'def': 315}" -micropython-v1_20_0-samd-seeed_wio_terminal-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-seeed_wio_terminal-stubs,1.20.0,samd,0.75,0.69,0.57,"{'def': 736, 'class': 89, 'module': 53}","{'module': 13, 'class': 28, 'def': 315}" -micropython-v1_20_0-stm32-pybv11-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-stm32-pybv11-stubs,1.20.0,stm32,0.77,0.75,0.66,"{'def': 1043, 'class': 109, 'module': 61}","{'module': 14, 'class': 27, 'def': 353}" -micropython-v1_20_0-stm32-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-stm32-stubs,1.20.0,stm32,0.77,0.75,0.66,"{'def': 1043, 'class': 109, 'module': 61}","{'module': 14, 'class': 27, 'def': 353}" -micropython-v1_21_0-esp32-esp32_generic-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-esp32-esp32_generic-stubs,1.21.0,esp32,0.64,0.6,0.59,"{'def': 1150, 'class': 145, 'module': 94}","{'module': 34, 'class': 58, 'def': 471}" -micropython-v1_21_0-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-esp32-stubs,1.21.0,esp32,0.64,0.6,0.59,"{'def': 1150, 'class': 145, 'module': 94}","{'module': 34, 'class': 58, 'def': 471}" -micropython-v1_21_0-rp2-rpi_pico-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-rp2-rpi_pico-stubs,1.21.0,rp2,0.76,0.58,0.58,"{'def': 838, 'class': 106, 'module': 67}","{'module': 16, 'class': 44, 'def': 356}" -micropython-v1_21_0-rp2-rpi_pico_w-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-rp2-rpi_pico_w-stubs,1.21.0,rp2,0.62,0.51,0.58,"{'def': 1170, 'class': 142, 'module': 93}","{'module': 35, 'class': 69, 'def': 492}" -micropython-v1_21_0-samd-seeed_wio_terminal-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-samd-seeed_wio_terminal-stubs,1.21.0,samd,0.72,0.59,0.58,"{'def': 766, 'class': 96, 'module': 60}","{'module': 17, 'class': 39, 'def': 325}" -micropython-v1_21_0-stm32-pybv11-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-stm32-pybv11-stubs,1.21.0,stm32,0.77,0.68,0.65,"{'def': 1089, 'class': 117, 'module': 64}","{'module': 15, 'class': 37, 'def': 385}" +micropython-v1_20_0-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-esp32-stubs,1.20.0,esp32,0.65,0.63,0.62,"{'def': 1010, 'class': 126, 'module': 85}","{'module': 30, 'class': 46, 'def': 384}" +micropython-v1_20_0-rp2-pico-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-pico-stubs,1.20.0,rp2,0.8,0.68,0.63,"{'def': 751, 'class': 93, 'module': 60}","{'module': 12, 'class': 30, 'def': 281}" +micropython-v1_20_0-rp2-pico_w-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-pico_w-stubs,1.20.0,rp2,0.75,0.66,0.61,"{'def': 865, 'class': 100, 'module': 71}","{'module': 18, 'class': 34, 'def': 340}" +micropython-v1_20_0-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-rp2-stubs,1.20.0,rp2,0.8,0.68,0.63,"{'def': 751, 'class': 93, 'module': 60}","{'module': 12, 'class': 30, 'def': 281}" +micropython-v1_20_0-samd-adafruit_feather_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-adafruit_feather_m4_express-stubs,1.20.0,samd,0.75,0.67,0.59,"{'def': 705, 'class': 85, 'module': 53}","{'module': 13, 'class': 28, 'def': 291}" +micropython-v1_20_0-samd-adafruit_itsybitsy_m4_express-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-adafruit_itsybitsy_m4_express-stubs,1.20.0,samd,0.75,0.67,0.59,"{'def': 705, 'class': 85, 'module': 53}","{'module': 13, 'class': 28, 'def': 291}" +micropython-v1_20_0-samd-minisam_m4-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-minisam_m4-stubs,1.20.0,samd,0.75,0.67,0.59,"{'def': 705, 'class': 85, 'module': 53}","{'module': 13, 'class': 28, 'def': 291}" +micropython-v1_20_0-samd-seeed_wio_terminal-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-seeed_wio_terminal-stubs,1.20.0,samd,0.75,0.67,0.59,"{'def': 705, 'class': 85, 'module': 53}","{'module': 13, 'class': 28, 'def': 291}" +micropython-v1_20_0-samd-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-samd-stubs,1.20.0,samd,0.75,0.67,0.59,"{'def': 705, 'class': 85, 'module': 53}","{'module': 13, 'class': 28, 'def': 291}" +micropython-v1_20_0-stm32-pybv11-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-stm32-pybv11-stubs,1.20.0,stm32,0.78,0.74,0.67,"{'def': 1011, 'class': 105, 'module': 60}","{'module': 13, 'class': 27, 'def': 329}" +micropython-v1_20_0-stm32-stubs,..\repos\micropython-stubs\publish\micropython-v1_20_0-stm32-stubs,1.20.0,stm32,0.78,0.74,0.67,"{'def': 1011, 'class': 105, 'module': 60}","{'module': 13, 'class': 27, 'def': 329}" +micropython-v1_21_0-esp32-esp32_generic-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-esp32-esp32_generic-stubs,1.21.0,esp32,0.65,0.6,0.61,"{'def': 1122, 'class': 141, 'module': 93}","{'module': 33, 'class': 57, 'def': 441}" +micropython-v1_21_0-esp32-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-esp32-stubs,1.21.0,esp32,0.65,0.6,0.61,"{'def': 1122, 'class': 141, 'module': 93}","{'module': 33, 'class': 57, 'def': 441}" +micropython-v1_21_0-rp2-rpi_pico-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-rp2-rpi_pico-stubs,1.21.0,rp2,0.77,0.63,0.63,"{'def': 810, 'class': 102, 'module': 66}","{'module': 15, 'class': 38, 'def': 299}" +micropython-v1_21_0-rp2-rpi_pico_w-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-rp2-rpi_pico_w-stubs,1.21.0,rp2,0.63,0.54,0.62,"{'def': 1142, 'class': 138, 'module': 92}","{'module': 34, 'class': 63, 'def': 435}" +micropython-v1_21_0-rp2-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-rp2-stubs,1.21.0,rp2,0.77,0.63,0.63,"{'def': 810, 'class': 102, 'module': 66}","{'module': 15, 'class': 38, 'def': 299}" +micropython-v1_21_0-samd-seeed_wio_terminal-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-samd-seeed_wio_terminal-stubs,1.21.0,samd,0.73,0.61,0.62,"{'def': 738, 'class': 92, 'module': 59}","{'module': 16, 'class': 36, 'def': 283}" +micropython-v1_21_0-samd-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-samd-stubs,1.21.0,samd,0.73,0.61,0.62,"{'def': 738, 'class': 92, 'module': 59}","{'module': 16, 'class': 36, 'def': 283}" +micropython-v1_21_0-stm32-pybv11-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-stm32-pybv11-stubs,1.21.0,stm32,0.78,0.7,0.68,"{'def': 1061, 'class': 113, 'module': 63}","{'module': 14, 'class': 34, 'def': 343}" +micropython-v1_21_0-stm32-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-stm32-stubs,1.21.0,stm32,0.78,0.7,0.68,"{'def': 1061, 'class': 113, 'module': 63}","{'module': 14, 'class': 34, 'def': 343}" +micropython-v1_21_0-webassembly-stubs,..\repos\micropython-stubs\publish\micropython-v1_21_0-webassembly-stubs,1.21.0,webassembly,0.95,0.7,0.69,"{'def': 316, 'class': 27, 'module': 40}","{'module': 2, 'class': 8, 'def': 98}" diff --git a/scripts/docstrings.json b/scripts/docstrings.json index 3d575660e..b238735e7 100644 --- a/scripts/docstrings.json +++ b/scripts/docstrings.json @@ -27,8 +27,8 @@ "class": 0.96, "def": 0.97, "counts": { - "def": 911, - "class": 116, + "def": 919, + "class": 120, "module": 55 }, "missing": { @@ -38,22 +38,22 @@ } }, { - "folder": "micropython-latest-esp32--OLD", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-esp32--OLD", + "folder": "micropython-latest-esp32-ESP32_GENERIC-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-esp32-ESP32_GENERIC-merged", "version": "99.99.99", "port": "esp32", - "module": 0.52, - "class": 0.29, - "def": 0.11, + "module": 0.84, + "class": 0.71, + "def": 0.59, "counts": { - "def": 2100, - "class": 331, - "module": 170 + "def": 2248, + "class": 385, + "module": 174 }, "missing": { - "module": 81, - "class": 234, - "def": 1874 + "module": 28, + "class": 110, + "def": 918 } }, { @@ -99,18 +99,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-frozen", "version": "99.99.99", "port": "frozen", - "module": 0.14, - "class": 0.12, - "def": 0.33, + "module": 0.26, + "class": 0.17, + "def": 0.37, "counts": { - "def": 19581, - "class": 2971, - "module": 2327 + "def": 20461, + "class": 3084, + "module": 2394 }, "missing": { - "module": 2004, - "class": 2626, - "def": 13158 + "module": 1778, + "class": 2555, + "def": 12977 } }, { @@ -151,6 +151,44 @@ "def": 660 } }, + { + "folder": "micropython-latest-rp2-RPI_PICO-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-rp2-RPI_PICO-merged", + "version": "99.99.99", + "port": "rp2", + "module": 0.89, + "class": 0.82, + "def": 0.62, + "counts": { + "def": 1688, + "class": 432, + "module": 128 + }, + "missing": { + "module": 14, + "class": 79, + "def": 644 + } + }, + { + "folder": "micropython-latest-rp2-RPI_PICO_W-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-rp2-RPI_PICO_W-merged", + "version": "99.99.99", + "port": "rp2", + "module": 0.83, + "class": 0.75, + "def": 0.57, + "counts": { + "def": 2288, + "class": 636, + "module": 174 + }, + "missing": { + "module": 30, + "class": 159, + "def": 978 + } + }, { "folder": "micropython-latest-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged", "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged", @@ -213,18 +251,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-samd-SEEED_WIO_TERMINAL-merged", "version": "99.99.99", "port": "samd", - "module": 0.88, + "module": 0.87, "class": 0.88, - "def": 0.56, + "def": 0.61, "counts": { - "def": 1454, - "class": 539, - "module": 102 + "def": 1546, + "class": 624, + "module": 114 }, "missing": { - "module": 12, - "class": 64, - "def": 646 + "module": 15, + "class": 76, + "def": 608 } }, { @@ -251,18 +289,37 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-stm32-PYBV11-merged", "version": "99.99.99", "port": "stm32", - "module": 0.92, + "module": 0.9, "class": 0.89, - "def": 0.66, + "def": 0.67, "counts": { - "def": 2066, - "class": 559, - "module": 118 + "def": 2190, + "class": 630, + "module": 124 }, "missing": { - "module": 10, - "class": 62, - "def": 708 + "module": 13, + "class": 72, + "def": 726 + } + }, + { + "folder": "micropython-latest-webassembly-GENERIC-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-latest-webassembly-GENERIC-merged", + "version": "99.99.99", + "port": "webassembly", + "module": 0.97, + "class": 0.77, + "def": 0.64, + "counts": { + "def": 688, + "class": 70, + "module": 80 + }, + "missing": { + "module": 2, + "class": 16, + "def": 246 } }, { @@ -702,25 +759,6 @@ "def": 2184 } }, - { - "folder": "micropython-v1_16-docstubs", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_16-docstubs", - "version": "1.16", - "port": "docstubs", - "module": 1.0, - "class": 1.0, - "def": 1.0, - "counts": { - "def": 0, - "class": 0, - "module": 0 - }, - "missing": { - "module": 0, - "class": 0, - "def": 0 - } - }, { "folder": "micropython-v1_16-esp32", "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_16-esp32", @@ -1221,11 +1259,11 @@ "version": "1.19.1", "port": "docstubs", "module": 1.0, - "class": 0.95, + "class": 0.96, "def": 0.97, "counts": { "def": 813, - "class": 109, + "class": 113, "module": 50 }, "missing": { @@ -1235,8 +1273,8 @@ } }, { - "folder": "micropython-v1_19_1-esp32", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32", + "folder": "micropython-v1_19_1-esp32-GENERIC", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-GENERIC", "version": "1.19.1", "port": "esp32", "module": 0.51, @@ -1254,16 +1292,16 @@ } }, { - "folder": "micropython-v1_19_1-esp32-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-merged", + "folder": "micropython-v1_19_1-esp32-GENERIC-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-GENERIC-merged", "version": "1.19.1", "port": "esp32", "module": 0.84, - "class": 0.73, + "class": 0.74, "def": 0.58, "counts": { "def": 1934, - "class": 305, + "class": 313, "module": 150 }, "missing": { @@ -1273,41 +1311,79 @@ } }, { - "folder": "micropython-v1_19_1-esp32-s3", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-s3", + "folder": "micropython-v1_19_1-esp32-GENERIC_S3", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-GENERIC_S3", "version": "1.19.1", "port": "esp32", - "module": 1.0, - "class": 0.33, - "def": 0.11, + "module": 0.52, + "class": 0.21, + "def": 0.1, "counts": { - "def": 1027, - "class": 167, - "module": 75 + "def": 1977, + "class": 268, + "module": 149 }, "missing": { - "module": 0, - "class": 112, - "def": 919 + "module": 71, + "class": 213, + "def": 1771 } }, { - "folder": "micropython-v1_19_1-esp32-S3-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-S3-merged", + "folder": "micropython-v1_19_1-esp32-GENERIC_S3-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-GENERIC_S3-merged", "version": "1.19.1", "port": "esp32", - "module": 1.0, - "class": 0.79, + "module": 0.86, + "class": 0.76, "def": 0.59, "counts": { - "def": 974, - "class": 178, - "module": 74 + "def": 1948, + "class": 311, + "module": 148 }, "missing": { - "module": 0, - "class": 37, - "def": 396 + "module": 20, + "class": 74, + "def": 792 + } + }, + { + "folder": "micropython-v1_19_1-esp32-GENERIC_SPIRAM", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-GENERIC_SPIRAM", + "version": "1.19.1", + "port": "esp32", + "module": 0.51, + "class": 0.21, + "def": 0.1, + "counts": { + "def": 1890, + "class": 260, + "module": 152 + }, + "missing": { + "module": 74, + "class": 205, + "def": 1694 + } + }, + { + "folder": "micropython-v1_19_1-esp32-GENERIC_SPIRAM-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-GENERIC_SPIRAM-merged", + "version": "1.19.1", + "port": "esp32", + "module": 0.84, + "class": 0.74, + "def": 0.58, + "counts": { + "def": 1934, + "class": 313, + "module": 150 + }, + "missing": { + "module": 24, + "class": 82, + "def": 806 } }, { @@ -1315,18 +1391,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-UM_TINYPICO", "version": "1.19.1", "port": "esp32", - "module": 0.91, - "class": 0.87, - "def": 0.12, + "module": 0.52, + "class": 0.61, + "def": 0.11, "counts": { - "def": 1021, - "class": 213, - "module": 79 + "def": 1862, + "class": 302, + "module": 147 }, "missing": { - "module": 7, - "class": 28, - "def": 898 + "module": 71, + "class": 117, + "def": 1654 } }, { @@ -1334,18 +1410,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-esp32-UM_TINYPICO-merged", "version": "1.19.1", "port": "esp32", - "module": 0.91, - "class": 0.88, - "def": 0.56, + "module": 0.84, + "class": 0.85, + "def": 0.6, "counts": { - "def": 1049, - "class": 232, - "module": 79 + "def": 1866, + "class": 328, + "module": 146 }, "missing": { - "module": 7, - "class": 28, - "def": 464 + "module": 24, + "class": 50, + "def": 744 } }, { @@ -1377,7 +1453,7 @@ "def": 0.59, "counts": { "def": 1412, - "class": 178, + "class": 186, "module": 118 }, "missing": { @@ -1391,18 +1467,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_19_1-frozen", "version": "1.19.1", "port": "frozen", - "module": 0.31, - "class": 0.35, - "def": 0.45, + "module": 0.23, + "class": 0.25, + "def": 0.41, "counts": { - "def": 4146, - "class": 721, - "module": 473 + "def": 8120, + "class": 1319, + "module": 935 }, "missing": { - "module": 325, - "class": 468, - "def": 2299 + "module": 720, + "class": 991, + "def": 4753 } }, { @@ -1430,17 +1506,17 @@ "version": "1.19.1", "port": "rp2", "module": 0.84, - "class": 0.65, - "def": 0.54, + "class": 0.67, + "def": 0.56, "counts": { "def": 1974, - "class": 395, + "class": 403, "module": 140 }, "missing": { "module": 22, - "class": 137, - "def": 902 + "class": 131, + "def": 874 } }, { @@ -1491,7 +1567,7 @@ "def": 0.64, "counts": { "def": 2088, - "class": 573, + "class": 581, "module": 114 }, "missing": { @@ -1506,11 +1582,11 @@ "version": "1.20.0", "port": "docstubs", "module": 1.0, - "class": 0.95, + "class": 0.96, "def": 0.97, "counts": { "def": 823, - "class": 108, + "class": 112, "module": 50 }, "missing": { @@ -1524,6 +1600,25 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32", "version": "1.20.0", "port": "esp32", + "module": 0.68, + "class": 0.49, + "def": 0.36, + "counts": { + "def": 3874, + "class": 568, + "module": 316 + }, + "missing": { + "module": 101, + "class": 290, + "def": 2474 + } + }, + { + "folder": "micropython-v1_20_0-esp32-GENERIC", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-GENERIC", + "version": "1.20.0", + "port": "esp32", "module": 0.52, "class": 0.21, "def": 0.1, @@ -1539,8 +1634,8 @@ } }, { - "folder": "micropython-v1_20_0-esp32-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-merged", + "folder": "micropython-v1_20_0-esp32-GENERIC-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-GENERIC-merged", "version": "1.20.0", "port": "esp32", "module": 0.84, @@ -1548,7 +1643,7 @@ "def": 0.61, "counts": { "def": 1966, - "class": 306, + "class": 314, "module": 158 }, "missing": { @@ -1558,8 +1653,8 @@ } }, { - "folder": "micropython-v1_20_0-esp32-OTA", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-OTA", + "folder": "micropython-v1_20_0-esp32-GENERIC_OTA", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-GENERIC_OTA", "version": "1.20.0", "port": "esp32", "module": 0.52, @@ -1577,8 +1672,8 @@ } }, { - "folder": "micropython-v1_20_0-esp32-OTA-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-OTA-merged", + "folder": "micropython-v1_20_0-esp32-GENERIC_OTA-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-GENERIC_OTA-merged", "version": "1.20.0", "port": "esp32", "module": 0.84, @@ -1586,7 +1681,7 @@ "def": 0.61, "counts": { "def": 1966, - "class": 306, + "class": 314, "module": 158 }, "missing": { @@ -1596,8 +1691,8 @@ } }, { - "folder": "micropython-v1_20_0-esp32-S3", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-S3", + "folder": "micropython-v1_20_0-esp32-GENERIC_S3", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-GENERIC_S3", "version": "1.20.0", "port": "esp32", "module": 0.52, @@ -1615,16 +1710,16 @@ } }, { - "folder": "micropython-v1_20_0-esp32-S3-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-S3-merged", + "folder": "micropython-v1_20_0-esp32-GENERIC_S3-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-esp32-GENERIC_S3-merged", "version": "1.20.0", "port": "esp32", "module": 0.85, - "class": 0.72, + "class": 0.73, "def": 0.61, "counts": { "def": 1944, - "class": 303, + "class": 311, "module": 156 }, "missing": { @@ -1639,55 +1734,17 @@ "version": "1.20.0", "port": "frozen", "module": 0.15, - "class": 0.15, + "class": 0.16, "def": 0.32, "counts": { "def": 18676, - "class": 2959, + "class": 2964, "module": 2019 }, "missing": { "module": 1711, - "class": 2517, - "def": 12774 - } - }, - { - "folder": "micropython-v1_20_0-rp2", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-rp2", - "version": "1.20.0", - "port": "rp2", - "module": 0.54, - "class": 0.52, - "def": 0.11, - "counts": { - "def": 1518, - "class": 339, - "module": 116 - }, - "missing": { - "module": 53, - "class": 164, - "def": 1354 - } - }, - { - "folder": "micropython-v1_20_0-rp2-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-rp2-merged", - "version": "1.20.0", - "port": "rp2", - "module": 0.91, - "class": 0.81, - "def": 0.57, - "counts": { - "def": 1546, - "class": 363, - "module": 116 - }, - "missing": { - "module": 11, - "class": 69, - "def": 660 + "class": 2475, + "def": 12639 } }, { @@ -1715,17 +1772,17 @@ "version": "1.20.0", "port": "rp2", "module": 0.91, - "class": 0.81, - "def": 0.57, + "class": 0.83, + "def": 0.59, "counts": { "def": 1546, - "class": 363, + "class": 371, "module": 116 }, "missing": { "module": 11, - "class": 69, - "def": 660 + "class": 63, + "def": 630 } }, { @@ -1753,17 +1810,17 @@ "version": "1.20.0", "port": "rp2", "module": 0.88, - "class": 0.8, - "def": 0.56, + "class": 0.82, + "def": 0.58, "counts": { "def": 1776, - "class": 389, + "class": 397, "module": 138 }, "missing": { "module": 17, - "class": 77, - "def": 780 + "class": 71, + "def": 750 } }, { @@ -1791,17 +1848,17 @@ "version": "1.20.0", "port": "rp2", "module": 0.91, - "class": 0.78, - "def": 0.57, + "class": 0.8, + "def": 0.59, "counts": { "def": 1546, - "class": 309, + "class": 317, "module": 116 }, "missing": { "module": 11, - "class": 69, - "def": 660 + "class": 63, + "def": 630 } }, { @@ -1833,7 +1890,7 @@ "def": 0.56, "counts": { "def": 1454, - "class": 401, + "class": 409, "module": 102 }, "missing": { @@ -1871,7 +1928,7 @@ "def": 0.56, "counts": { "def": 1454, - "class": 375, + "class": 383, "module": 102 }, "missing": { @@ -1905,11 +1962,11 @@ "version": "1.20.0", "port": "samd", "module": 0.88, - "class": 0.83, + "class": 0.84, "def": 0.56, "counts": { "def": 1454, - "class": 357, + "class": 365, "module": 102 }, "missing": { @@ -1947,7 +2004,7 @@ "def": 0.56, "counts": { "def": 1454, - "class": 543, + "class": 551, "module": 102 }, "missing": { @@ -1976,32 +2033,13 @@ } }, { - "folder": "micropython-v1_20_0-stm32-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-stm32-merged", + "folder": "micropython-v1_20_0-stm32-PYBV11", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-stm32-PYBV11", "version": "1.20.0", "port": "stm32", - "module": 0.9, - "class": 0.9, - "def": 0.65, - "counts": { - "def": 2066, - "class": 563, - "module": 118 - }, - "missing": { - "module": 12, - "class": 58, - "def": 720 - } - }, - { - "folder": "micropython-v1_20_0-stm32-PYBV11", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_20_0-stm32-PYBV11", - "version": "1.20.0", - "port": "stm32", - "module": 0.53, - "class": 0.66, - "def": 0.09, + "module": 0.53, + "class": 0.66, + "def": 0.09, "counts": { "def": 2034, "class": 539, @@ -2023,7 +2061,7 @@ "def": 0.65, "counts": { "def": 2066, - "class": 563, + "class": 571, "module": 118 }, "missing": { @@ -2042,7 +2080,7 @@ "def": 0.97, "counts": { "def": 915, - "class": 124, + "class": 128, "module": 55 }, "missing": { @@ -2051,25 +2089,6 @@ "def": 24 } }, - { - "folder": "micropython-v1_21_0-esp32", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-esp32", - "version": "1.21.0", - "port": "esp32", - "module": 0.51, - "class": 0.29, - "def": 0.1, - "counts": { - "def": 2220, - "class": 339, - "module": 179 - }, - "missing": { - "module": 87, - "class": 242, - "def": 1988 - } - }, { "folder": "micropython-v1_21_0-esp32-ESP32_GENERIC", "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-esp32-ESP32_GENERIC", @@ -2094,37 +2113,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-esp32-ESP32_GENERIC-merged", "version": "1.21.0", "port": "esp32", - "module": 0.83, - "class": 0.7, - "def": 0.58, - "counts": { - "def": 2236, - "class": 377, - "module": 174 - }, - "missing": { - "module": 30, - "class": 112, - "def": 950 - } - }, - { - "folder": "micropython-v1_21_0-esp32-merged", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-esp32-merged", - "version": "1.21.0", - "port": "esp32", - "module": 0.83, - "class": 0.7, - "def": 0.58, + "module": 0.84, + "class": 0.71, + "def": 0.59, "counts": { - "def": 2236, - "class": 377, + "def": 2248, + "class": 385, "module": 174 }, "missing": { - "module": 30, - "class": 112, - "def": 950 + "module": 28, + "class": 110, + "def": 918 } }, { @@ -2133,17 +2133,17 @@ "version": "1.21.0", "port": "frozen", "module": 0.23, - "class": 0.15, + "class": 0.17, "def": 0.36, "counts": { "def": 19937, - "class": 2989, + "class": 2996, "module": 2338 }, "missing": { "module": 1795, - "class": 2554, - "def": 12856 + "class": 2494, + "def": 12679 } }, { @@ -2170,18 +2170,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-rp2-RPI_PICO-merged", "version": "1.21.0", "port": "rp2", - "module": 0.88, - "class": 0.78, - "def": 0.55, + "module": 0.89, + "class": 0.82, + "def": 0.62, "counts": { - "def": 1676, - "class": 424, + "def": 1688, + "class": 432, "module": 128 }, "missing": { - "module": 16, - "class": 95, - "def": 754 + "module": 14, + "class": 79, + "def": 644 } }, { @@ -2208,18 +2208,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-rp2-RPI_PICO_W-merged", "version": "1.21.0", "port": "rp2", - "module": 0.82, - "class": 0.72, - "def": 0.52, + "module": 0.83, + "class": 0.75, + "def": 0.57, "counts": { - "def": 2276, - "class": 628, + "def": 2288, + "class": 636, "module": 174 }, "missing": { - "module": 32, - "class": 175, - "def": 1088 + "module": 30, + "class": 159, + "def": 978 } }, { @@ -2228,17 +2228,17 @@ "version": "1.21.0", "port": "samd", "module": 0.52, - "class": 0.72, + "class": 0.71, "def": 0.11, "counts": { - "def": 1506, - "class": 594, - "module": 114 + "def": 1534, + "class": 596, + "module": 116 }, "missing": { - "module": 55, - "class": 168, - "def": 1340 + "module": 56, + "class": 170, + "def": 1366 } }, { @@ -2246,18 +2246,18 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged", "version": "1.21.0", "port": "samd", - "module": 0.85, - "class": 0.86, - "def": 0.55, + "module": 0.86, + "class": 0.88, + "def": 0.6, "counts": { - "def": 1534, - "class": 616, - "module": 114 + "def": 1574, + "class": 626, + "module": 116 }, "missing": { - "module": 17, - "class": 86, - "def": 688 + "module": 16, + "class": 78, + "def": 634 } }, { @@ -2284,227 +2284,607 @@ "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-stm32-PYBV11-merged", "version": "1.21.0", "port": "stm32", - "module": 0.88, - "class": 0.87, - "def": 0.63, + "module": 0.9, + "class": 0.89, + "def": 0.67, "counts": { - "def": 2178, - "class": 622, + "def": 2190, + "class": 630, "module": 124 }, "missing": { - "module": 15, - "class": 82, - "def": 806 + "module": 13, + "class": 72, + "def": 726 } }, { - "folder": "micropython-v1_9_3-esp8266", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_3-esp8266", - "version": "1.9.3", - "port": "esp8266", + "folder": "micropython-v1_21_0-webassembly-GENERIC", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-webassembly-GENERIC", + "version": "1.21.0", + "port": "webassembly", "module": 0.51, - "class": 0.58, - "def": 0.01, + "class": 0.0, + "def": 0.07, "counts": { - "def": 1112, - "class": 182, - "module": 117 + "def": 664, + "class": 44, + "module": 80 }, "missing": { - "module": 57, - "class": 76, - "def": 1100 + "module": 39, + "class": 44, + "def": 620 } }, { - "folder": "micropython-v1_9_3-frozen", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_3-frozen", - "version": "1.9.3", - "port": "frozen", - "module": 0.05, - "class": 0.0, - "def": 0.16, + "folder": "micropython-v1_21_0-webassembly-GENERIC-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_21_0-webassembly-GENERIC-merged", + "version": "1.21.0", + "port": "webassembly", + "module": 0.97, + "class": 0.77, + "def": 0.64, "counts": { - "def": 363, - "class": 40, - "module": 40 + "def": 688, + "class": 70, + "module": 80 }, "missing": { - "module": 38, - "class": 40, - "def": 306 + "module": 2, + "class": 16, + "def": 246 } }, { - "folder": "micropython-v1_9_4-esp8266", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_4-esp8266", - "version": "1.9.4", - "port": "esp8266", - "module": 0.52, - "class": 0.5, - "def": 0.02, + "folder": "micropython-v1_22_0-docstubs", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-docstubs", + "version": "1.22.0", + "port": "docstubs", + "module": 1.0, + "class": 0.96, + "def": 0.97, "counts": { - "def": 642, - "class": 104, - "module": 119 + "def": 919, + "class": 120, + "module": 55 }, "missing": { - "module": 57, - "class": 52, - "def": 630 + "module": 0, + "class": 5, + "def": 24 } }, { - "folder": "micropython-v1_9_4-frozen", - "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_4-frozen", - "version": "1.9.4", - "port": "frozen", - "module": 0.05, - "class": 0.0, - "def": 0.17, + "folder": "micropython-v1_22_0-esp32-ESP32_GENERIC", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-esp32-ESP32_GENERIC", + "version": "1.22.0", + "port": "esp32", + "module": 0.52, + "class": 0.3, + "def": 0.11, "counts": { - "def": 571, - "class": 82, - "module": 74 + "def": 2242, + "class": 353, + "module": 178 }, "missing": { - "module": 70, - "class": 82, - "def": 474 + "module": 85, + "class": 248, + "def": 2002 } }, { - "folder": "micropython-latest-esp32-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-esp32-stubs", - "version": "99.99.99", + "folder": "micropython-v1_22_0-esp32-ESP32_GENERIC-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-esp32-ESP32_GENERIC-merged", + "version": "1.22.0", "port": "esp32", - "module": 0.61, - "class": 0.59, + "module": 0.84, + "class": 0.72, "def": 0.59, "counts": { - "def": 1148, - "class": 145, - "module": 94 + "def": 2274, + "class": 397, + "module": 174 }, "missing": { - "module": 37, - "class": 59, - "def": 475 + "module": 28, + "class": 112, + "def": 922 } }, { - "folder": "micropython-latest-rp2-pimoroni_picolipo_16mb-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-rp2-pimoroni_picolipo_16mb-stubs", - "version": "99.99.99", - "port": "rp2", - "module": 0.7, - "class": 0.55, - "def": 0.55, + "folder": "micropython-v1_22_0-esp32-ESP32_GENERIC_S3", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-esp32-ESP32_GENERIC_S3", + "version": "1.22.0", + "port": "esp32", + "module": 0.52, + "class": 0.3, + "def": 0.11, "counts": { - "def": 844, - "class": 107, - "module": 66 + "def": 2232, + "class": 349, + "module": 178 }, "missing": { - "module": 20, - "class": 48, - "def": 377 + "module": 85, + "class": 244, + "def": 1996 } }, { - "folder": "micropython-latest-rp2-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-rp2-stubs", - "version": "99.99.99", - "port": "rp2", - "module": 0.7, - "class": 0.55, - "def": 0.55, + "folder": "micropython-v1_22_0-esp32-ESP32_GENERIC_S3-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-esp32-ESP32_GENERIC_S3-merged", + "version": "1.22.0", + "port": "esp32", + "module": 0.84, + "class": 0.73, + "def": 0.59, "counts": { - "def": 844, - "class": 107, - "module": 66 + "def": 2264, + "class": 393, + "module": 174 }, "missing": { - "module": 20, - "class": 48, - "def": 377 + "module": 28, + "class": 108, + "def": 918 } }, { - "folder": "micropython-latest-samd-adafruit_feather_m4_express-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-adafruit_feather_m4_express-stubs", - "version": "99.99.99", - "port": "samd", - "module": 0.68, - "class": 0.58, - "def": 0.54, + "folder": "micropython-v1_22_0-esp32-Generic_ESP32S3_module", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-esp32-Generic_ESP32S3_module", + "version": "1.22.0", + "port": "esp32", + "module": 0.52, + "class": 0.3, + "def": 0.11, "counts": { - "def": 798, - "class": 99, - "module": 59 + "def": 2232, + "class": 349, + "module": 178 }, "missing": { - "module": 19, - "class": 42, - "def": 368 + "module": 85, + "class": 244, + "def": 1996 } }, { - "folder": "micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs", - "version": "99.99.99", - "port": "samd", - "module": 0.68, - "class": 0.58, - "def": 0.54, + "folder": "micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT", + "version": "1.22.0", + "port": "rp2", + "module": 0.53, + "class": 0.52, + "def": 0.12, "counts": { - "def": 798, - "class": 99, - "module": 59 + "def": 2292, + "class": 616, + "module": 180 }, "missing": { - "module": 19, - "class": 42, - "def": 368 + "module": 85, + "class": 298, + "def": 2010 } }, { - "folder": "micropython-latest-samd-minisam_m4-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-minisam_m4-stubs", - "version": "99.99.99", - "port": "samd", - "module": 0.68, - "class": 0.58, - "def": 0.54, + "folder": "micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-rp2-ARDUINO_NANO_RP2040_CONNECT-merged", + "version": "1.22.0", + "port": "rp2", + "module": 0.82, + "class": 0.75, + "def": 0.58, "counts": { - "def": 798, - "class": 99, - "module": 59 + "def": 2312, + "class": 646, + "module": 176 }, "missing": { - "module": 19, - "class": 42, - "def": 368 + "module": 31, + "class": 161, + "def": 976 } }, { - "folder": "micropython-latest-samd-seeed_wio_terminal-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-seeed_wio_terminal-stubs", - "version": "99.99.99", + "folder": "micropython-v1_22_0-rp2-RPI_PICO", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-rp2-RPI_PICO", + "version": "1.22.0", + "port": "rp2", + "module": 0.54, + "class": 0.53, + "def": 0.12, + "counts": { + "def": 1612, + "class": 408, + "module": 130 + }, + "missing": { + "module": 60, + "class": 192, + "def": 1424 + } + }, + { + "folder": "micropython-v1_22_0-rp2-RPI_PICO-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-rp2-RPI_PICO-merged", + "version": "1.22.0", + "port": "rp2", + "module": 0.88, + "class": 0.82, + "def": 0.62, + "counts": { + "def": 1652, + "class": 438, + "module": 130 + }, + "missing": { + "module": 15, + "class": 79, + "def": 632 + } + }, + { + "folder": "micropython-v1_22_0-rp2-RPI_PICO_W", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-rp2-RPI_PICO_W", + "version": "1.22.0", + "port": "rp2", + "module": 0.53, + "class": 0.52, + "def": 0.12, + "counts": { + "def": 2228, + "class": 590, + "module": 180 + }, + "missing": { + "module": 85, + "class": 284, + "def": 1960 + } + }, + { + "folder": "micropython-v1_22_0-rp2-RPI_PICO_W-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-rp2-RPI_PICO_W-merged", + "version": "1.22.0", + "port": "rp2", + "module": 0.82, + "class": 0.76, + "def": 0.58, + "counts": { + "def": 2248, + "class": 620, + "module": 176 + }, + "missing": { + "module": 31, + "class": 151, + "def": 954 + } + }, + { + "folder": "micropython-v1_22_0-samd-SEEED_WIO_TERMINAL", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-samd-SEEED_WIO_TERMINAL", + "version": "1.22.0", "port": "samd", + "module": 0.52, + "class": 0.72, + "def": 0.11, + "counts": { + "def": 1488, + "class": 604, + "module": 116 + }, + "missing": { + "module": 56, + "class": 172, + "def": 1318 + } + }, + { + "folder": "micropython-v1_22_0-samd-SEEED_WIO_TERMINAL-merged", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_22_0-samd-SEEED_WIO_TERMINAL-merged", + "version": "1.22.0", + "port": "samd", + "module": 0.86, + "class": 0.88, + "def": 0.6, + "counts": { + "def": 1528, + "class": 634, + "module": 116 + }, + "missing": { + "module": 16, + "class": 76, + "def": 614 + } + }, + { + "folder": "micropython-v1_9_3-esp8266", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_3-esp8266", + "version": "1.9.3", + "port": "esp8266", + "module": 0.51, + "class": 0.58, + "def": 0.01, + "counts": { + "def": 1112, + "class": 182, + "module": 117 + }, + "missing": { + "module": 57, + "class": 76, + "def": 1100 + } + }, + { + "folder": "micropython-v1_9_3-frozen", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_3-frozen", + "version": "1.9.3", + "port": "frozen", + "module": 0.05, + "class": 0.0, + "def": 0.16, + "counts": { + "def": 363, + "class": 40, + "module": 40 + }, + "missing": { + "module": 38, + "class": 40, + "def": 306 + } + }, + { + "folder": "micropython-v1_9_4-esp8266", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_4-esp8266", + "version": "1.9.4", + "port": "esp8266", + "module": 0.52, + "class": 0.5, + "def": 0.02, + "counts": { + "def": 642, + "class": 104, + "module": 119 + }, + "missing": { + "module": 57, + "class": 52, + "def": 630 + } + }, + { + "folder": "micropython-v1_9_4-frozen", + "path": "..\\repos\\micropython-stubs\\stubs\\micropython-v1_9_4-frozen", + "version": "1.9.4", + "port": "frozen", + "module": 0.05, + "class": 0.0, + "def": 0.17, + "counts": { + "def": 571, + "class": 82, + "module": 74 + }, + "missing": { + "module": 70, + "class": 82, + "def": 474 + } + }, + { + "folder": "micropython-latest-esp32-esp32_generic-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-esp32-esp32_generic-stubs", + "version": "99.99.99", + "port": "esp32", "module": 0.68, + "class": 0.6, + "def": 0.6, + "counts": { + "def": 1096, + "class": 138, + "module": 87 + }, + "missing": { + "module": 28, + "class": 55, + "def": 435 + } + }, + { + "folder": "micropython-latest-esp32-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-esp32-stubs", + "version": "99.99.99", + "port": "esp32", + "module": 0.68, + "class": 0.6, + "def": 0.6, + "counts": { + "def": 1096, + "class": 138, + "module": 87 + }, + "missing": { + "module": 28, + "class": 55, + "def": 435 + } + }, + { + "folder": "micropython-latest-rp2-pimoroni_picolipo_16mb-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-rp2-pimoroni_picolipo_16mb-stubs", + "version": "99.99.99", + "port": "rp2", + "module": 0.81, "class": 0.58, - "def": 0.54, + "def": 0.59, "counts": { - "def": 798, - "class": 99, - "module": 59 + "def": 745, + "class": 88, + "module": 58 + }, + "missing": { + "module": 11, + "class": 37, + "def": 306 + } + }, + { + "folder": "micropython-latest-rp2-rpi_pico-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-rp2-rpi_pico-stubs", + "version": "99.99.99", + "port": "rp2", + "module": 0.78, + "class": 0.62, + "def": 0.63, + "counts": { + "def": 816, + "class": 105, + "module": 64 + }, + "missing": { + "module": 14, + "class": 40, + "def": 298 + } + }, + { + "folder": "micropython-latest-rp2-rpi_pico_w-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-rp2-rpi_pico_w-stubs", + "version": "99.99.99", + "port": "rp2", + "module": 0.64, + "class": 0.54, + "def": 0.62, + "counts": { + "def": 1142, + "class": 138, + "module": 92 + }, + "missing": { + "module": 33, + "class": 63, + "def": 435 + } + }, + { + "folder": "micropython-latest-rp2-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-rp2-stubs", + "version": "99.99.99", + "port": "rp2", + "module": 0.78, + "class": 0.62, + "def": 0.63, + "counts": { + "def": 816, + "class": 105, + "module": 64 + }, + "missing": { + "module": 14, + "class": 40, + "def": 298 + } + }, + { + "folder": "micropython-latest-samd-adafruit_feather_m4_express-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-adafruit_feather_m4_express-stubs", + "version": "99.99.99", + "port": "samd", + "module": 0.76, + "class": 0.6, + "def": 0.57, + "counts": { + "def": 700, + "class": 80, + "module": 51 + }, + "missing": { + "module": 12, + "class": 32, + "def": 299 + } + }, + { + "folder": "micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-adafruit_itsybitsy_m4_express-stubs", + "version": "99.99.99", + "port": "samd", + "module": 0.76, + "class": 0.6, + "def": 0.57, + "counts": { + "def": 700, + "class": 80, + "module": 51 }, "missing": { - "module": 19, - "class": 42, - "def": 368 + "module": 12, + "class": 32, + "def": 299 + } + }, + { + "folder": "micropython-latest-samd-minisam_m4-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-minisam_m4-stubs", + "version": "99.99.99", + "port": "samd", + "module": 0.76, + "class": 0.6, + "def": 0.57, + "counts": { + "def": 700, + "class": 80, + "module": 51 + }, + "missing": { + "module": 12, + "class": 32, + "def": 299 + } + }, + { + "folder": "micropython-latest-samd-seeed_wio_terminal-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-seeed_wio_terminal-stubs", + "version": "99.99.99", + "port": "samd", + "module": 0.74, + "class": 0.6, + "def": 0.62, + "counts": { + "def": 745, + "class": 95, + "module": 57 + }, + "missing": { + "module": 15, + "class": 38, + "def": 280 + } + }, + { + "folder": "micropython-latest-samd-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-samd-stubs", + "version": "99.99.99", + "port": "samd", + "module": 0.74, + "class": 0.6, + "def": 0.62, + "counts": { + "def": 745, + "class": 95, + "module": 57 + }, + "missing": { + "module": 15, + "class": 38, + "def": 280 } }, { @@ -2512,18 +2892,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-stm32-pybv11-stubs", "version": "99.99.99", "port": "stm32", - "module": 0.74, - "class": 0.66, - "def": 0.64, + "module": 0.79, + "class": 0.69, + "def": 0.68, "counts": { - "def": 1104, - "class": 119, - "module": 66 + "def": 1067, + "class": 116, + "module": 62 }, "missing": { - "module": 17, - "class": 41, - "def": 400 + "module": 13, + "class": 36, + "def": 339 } }, { @@ -2531,18 +2911,37 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-stm32-stubs", "version": "99.99.99", "port": "stm32", - "module": 0.74, - "class": 0.66, - "def": 0.64, + "module": 0.79, + "class": 0.69, + "def": 0.68, "counts": { - "def": 1104, - "class": 119, - "module": 66 + "def": 1067, + "class": 116, + "module": 62 }, "missing": { - "module": 17, - "class": 41, - "def": 400 + "module": 13, + "class": 36, + "def": 339 + } + }, + { + "folder": "micropython-latest-webassembly-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-latest-webassembly-stubs", + "version": "99.99.99", + "port": "webassembly", + "module": 0.95, + "class": 0.7, + "def": 0.69, + "counts": { + "def": 316, + "class": 27, + "module": 40 + }, + "missing": { + "module": 2, + "class": 8, + "def": 98 } }, { @@ -2554,9 +2953,9 @@ "class": 1.0, "def": 1.0, "counts": { - "def": 3625, - "class": 658, - "module": 63 + "def": 3566, + "class": 649, + "module": 57 }, "missing": { "module": 0, @@ -2716,6 +3115,44 @@ "def": 371 } }, + { + "folder": "micropython-v1_19_1-esp32-generic_s3-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-esp32-generic_s3-stubs", + "version": "1.19.1", + "port": "esp32", + "module": 0.64, + "class": 0.62, + "def": 0.58, + "counts": { + "def": 1048, + "class": 136, + "module": 86 + }, + "missing": { + "module": 31, + "class": 51, + "def": 443 + } + }, + { + "folder": "micropython-v1_19_1-esp32-generic_spiram-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-esp32-generic_spiram-stubs", + "version": "1.19.1", + "port": "esp32", + "module": 0.65, + "class": 0.66, + "def": 0.59, + "counts": { + "def": 1003, + "class": 129, + "module": 82 + }, + "missing": { + "module": 29, + "class": 44, + "def": 416 + } + }, { "folder": "micropython-v1_19_1-esp32-s3-stubs", "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-esp32-s3-stubs", @@ -2740,18 +3177,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-esp32-stubs", "version": "1.19.1", "port": "esp32", - "module": 0.64, - "class": 0.65, - "def": 0.57, + "module": 0.65, + "class": 0.66, + "def": 0.59, "counts": { - "def": 1020, - "class": 133, - "module": 81 + "def": 1003, + "class": 129, + "module": 82 }, "missing": { "module": 29, - "class": 47, - "def": 443 + "class": 44, + "def": 416 } }, { @@ -2759,18 +3196,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-esp32-um_tinypico-stubs", "version": "1.19.1", "port": "esp32", - "module": 0.63, + "module": 0.62, "class": 0.65, - "def": 0.57, + "def": 0.63, "counts": { - "def": 1038, - "class": 134, - "module": 83 + "def": 931, + "class": 110, + "module": 82 }, "missing": { "module": 31, - "class": 47, - "def": 443 + "class": 38, + "def": 342 } }, { @@ -2778,56 +3215,94 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-esp8266-stubs", "version": "1.19.1", "port": "esp8266", - "module": 0.71, - "class": 0.72, - "def": 0.58, + "module": 0.73, + "class": 0.73, + "def": 0.59, "counts": { - "def": 739, + "def": 724, "class": 85, - "module": 65 + "module": 66 }, "missing": { - "module": 19, - "class": 24, - "def": 311 + "module": 18, + "class": 23, + "def": 296 + } + }, + { + "folder": "micropython-v1_19_1-rp2-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-rp2-stubs", + "version": "1.19.1", + "port": "rp2", + "module": 0.69, + "class": 0.53, + "def": 0.58, + "counts": { + "def": 981, + "class": 137, + "module": 74 + }, + "missing": { + "module": 23, + "class": 64, + "def": 413 + } + }, + { + "folder": "micropython-v1_19_1-stm32-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-stm32-stubs", + "version": "1.19.1", + "port": "stm32", + "module": 0.77, + "class": 0.75, + "def": 0.66, + "counts": { + "def": 1043, + "class": 114, + "module": 60 + }, + "missing": { + "module": 14, + "class": 28, + "def": 358 } }, { - "folder": "micropython-v1_19_1-rp2-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-rp2-stubs", - "version": "1.19.1", - "port": "rp2", - "module": 0.68, - "class": 0.5, - "def": 0.54, + "folder": "micropython-v1_20_0-esp32-generic_ota-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-esp32-generic_ota-stubs", + "version": "1.20.0", + "port": "esp32", + "module": 0.65, + "class": 0.63, + "def": 0.62, "counts": { - "def": 1003, - "class": 142, - "module": 73 + "def": 1010, + "class": 126, + "module": 85 }, "missing": { - "module": 23, - "class": 71, - "def": 460 + "module": 30, + "class": 46, + "def": 384 } }, { - "folder": "micropython-v1_19_1-stm32-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_19_1-stm32-stubs", - "version": "1.19.1", - "port": "stm32", - "module": 0.76, - "class": 0.73, - "def": 0.63, + "folder": "micropython-v1_20_0-esp32-generic_s3-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-esp32-generic_s3-stubs", + "version": "1.20.0", + "port": "esp32", + "module": 0.65, + "class": 0.64, + "def": 0.62, "counts": { - "def": 1066, - "class": 119, - "module": 59 + "def": 980, + "class": 120, + "module": 84 }, "missing": { - "module": 14, - "class": 32, - "def": 391 + "module": 29, + "class": 43, + "def": 369 } }, { @@ -2873,18 +3348,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-esp32-stubs", "version": "1.20.0", "port": "esp32", - "module": 0.64, - "class": 0.64, - "def": 0.61, + "module": 0.65, + "class": 0.63, + "def": 0.62, "counts": { - "def": 1041, - "class": 130, + "def": 1010, + "class": 126, "module": 85 }, "missing": { - "module": 31, - "class": 47, - "def": 410 + "module": 30, + "class": 46, + "def": 384 } }, { @@ -2892,18 +3367,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-rp2-pico-stubs", "version": "1.20.0", "port": "rp2", - "module": 0.77, - "class": 0.65, - "def": 0.59, + "module": 0.8, + "class": 0.68, + "def": 0.63, "counts": { - "def": 782, - "class": 97, + "def": 751, + "class": 93, "module": 60 }, "missing": { - "module": 14, - "class": 34, - "def": 324 + "module": 12, + "class": 30, + "def": 281 } }, { @@ -2911,37 +3386,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-rp2-pico_w-stubs", "version": "1.20.0", "port": "rp2", - "module": 0.72, - "class": 0.63, - "def": 0.57, + "module": 0.75, + "class": 0.66, + "def": 0.61, "counts": { - "def": 896, - "class": 104, + "def": 865, + "class": 100, "module": 71 }, "missing": { - "module": 20, - "class": 38, - "def": 383 - } - }, - { - "folder": "micropython-v1_20_0-rp2-pimoroni_picolipo_16mb-stubs", - "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-rp2-pimoroni_picolipo_16mb-stubs", - "version": "1.20.0", - "port": "rp2", - "module": 0.77, - "class": 0.65, - "def": 0.59, - "counts": { - "def": 782, - "class": 97, - "module": 60 - }, - "missing": { - "module": 14, + "module": 18, "class": 34, - "def": 324 + "def": 340 } }, { @@ -2949,18 +3405,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-rp2-stubs", "version": "1.20.0", "port": "rp2", - "module": 0.77, - "class": 0.65, - "def": 0.59, + "module": 0.8, + "class": 0.68, + "def": 0.63, "counts": { - "def": 782, - "class": 97, + "def": 751, + "class": 93, "module": 60 }, "missing": { - "module": 14, - "class": 34, - "def": 324 + "module": 12, + "class": 30, + "def": 281 } }, { @@ -2969,17 +3425,17 @@ "version": "1.20.0", "port": "samd", "module": 0.75, - "class": 0.69, - "def": 0.57, + "class": 0.67, + "def": 0.59, "counts": { - "def": 736, - "class": 89, + "def": 705, + "class": 85, "module": 53 }, "missing": { "module": 13, "class": 28, - "def": 315 + "def": 291 } }, { @@ -2988,17 +3444,17 @@ "version": "1.20.0", "port": "samd", "module": 0.75, - "class": 0.69, - "def": 0.57, + "class": 0.67, + "def": 0.59, "counts": { - "def": 736, - "class": 89, + "def": 705, + "class": 85, "module": 53 }, "missing": { "module": 13, "class": 28, - "def": 315 + "def": 291 } }, { @@ -3007,17 +3463,17 @@ "version": "1.20.0", "port": "samd", "module": 0.75, - "class": 0.69, - "def": 0.57, + "class": 0.67, + "def": 0.59, "counts": { - "def": 736, - "class": 89, + "def": 705, + "class": 85, "module": 53 }, "missing": { "module": 13, "class": 28, - "def": 315 + "def": 291 } }, { @@ -3026,17 +3482,36 @@ "version": "1.20.0", "port": "samd", "module": 0.75, - "class": 0.69, - "def": 0.57, + "class": 0.67, + "def": 0.59, "counts": { - "def": 736, - "class": 89, + "def": 705, + "class": 85, + "module": 53 + }, + "missing": { + "module": 13, + "class": 28, + "def": 291 + } + }, + { + "folder": "micropython-v1_20_0-samd-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-samd-stubs", + "version": "1.20.0", + "port": "samd", + "module": 0.75, + "class": 0.67, + "def": 0.59, + "counts": { + "def": 705, + "class": 85, "module": 53 }, "missing": { "module": 13, "class": 28, - "def": 315 + "def": 291 } }, { @@ -3044,18 +3519,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-stm32-pybv11-stubs", "version": "1.20.0", "port": "stm32", - "module": 0.77, - "class": 0.75, - "def": 0.66, + "module": 0.78, + "class": 0.74, + "def": 0.67, "counts": { - "def": 1043, - "class": 109, - "module": 61 + "def": 1011, + "class": 105, + "module": 60 }, "missing": { - "module": 14, + "module": 13, "class": 27, - "def": 353 + "def": 329 } }, { @@ -3063,18 +3538,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_20_0-stm32-stubs", "version": "1.20.0", "port": "stm32", - "module": 0.77, - "class": 0.75, - "def": 0.66, + "module": 0.78, + "class": 0.74, + "def": 0.67, "counts": { - "def": 1043, - "class": 109, - "module": 61 + "def": 1011, + "class": 105, + "module": 60 }, "missing": { - "module": 14, + "module": 13, "class": 27, - "def": 353 + "def": 329 } }, { @@ -3082,18 +3557,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-esp32-esp32_generic-stubs", "version": "1.21.0", "port": "esp32", - "module": 0.64, + "module": 0.65, "class": 0.6, - "def": 0.59, + "def": 0.61, "counts": { - "def": 1150, - "class": 145, - "module": 94 + "def": 1122, + "class": 141, + "module": 93 }, "missing": { - "module": 34, - "class": 58, - "def": 471 + "module": 33, + "class": 57, + "def": 441 } }, { @@ -3101,18 +3576,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-esp32-stubs", "version": "1.21.0", "port": "esp32", - "module": 0.64, + "module": 0.65, "class": 0.6, - "def": 0.59, + "def": 0.61, "counts": { - "def": 1150, - "class": 145, - "module": 94 + "def": 1122, + "class": 141, + "module": 93 }, "missing": { - "module": 34, - "class": 58, - "def": 471 + "module": 33, + "class": 57, + "def": 441 } }, { @@ -3120,18 +3595,18 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-rp2-rpi_pico-stubs", "version": "1.21.0", "port": "rp2", - "module": 0.76, - "class": 0.58, - "def": 0.58, + "module": 0.77, + "class": 0.63, + "def": 0.63, "counts": { - "def": 838, - "class": 106, - "module": 67 + "def": 810, + "class": 102, + "module": 66 }, "missing": { - "module": 16, - "class": 44, - "def": 356 + "module": 15, + "class": 38, + "def": 299 } }, { @@ -3139,18 +3614,37 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-rp2-rpi_pico_w-stubs", "version": "1.21.0", "port": "rp2", - "module": 0.62, - "class": 0.51, - "def": 0.58, + "module": 0.63, + "class": 0.54, + "def": 0.62, "counts": { - "def": 1170, - "class": 142, - "module": 93 + "def": 1142, + "class": 138, + "module": 92 }, "missing": { - "module": 35, - "class": 69, - "def": 492 + "module": 34, + "class": 63, + "def": 435 + } + }, + { + "folder": "micropython-v1_21_0-rp2-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-rp2-stubs", + "version": "1.21.0", + "port": "rp2", + "module": 0.77, + "class": 0.63, + "def": 0.63, + "counts": { + "def": 810, + "class": 102, + "module": 66 + }, + "missing": { + "module": 15, + "class": 38, + "def": 299 } }, { @@ -3158,18 +3652,37 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-samd-seeed_wio_terminal-stubs", "version": "1.21.0", "port": "samd", - "module": 0.72, - "class": 0.59, - "def": 0.58, + "module": 0.73, + "class": 0.61, + "def": 0.62, "counts": { - "def": 766, - "class": 96, - "module": 60 + "def": 738, + "class": 92, + "module": 59 }, "missing": { - "module": 17, - "class": 39, - "def": 325 + "module": 16, + "class": 36, + "def": 283 + } + }, + { + "folder": "micropython-v1_21_0-samd-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-samd-stubs", + "version": "1.21.0", + "port": "samd", + "module": 0.73, + "class": 0.61, + "def": 0.62, + "counts": { + "def": 738, + "class": 92, + "module": 59 + }, + "missing": { + "module": 16, + "class": 36, + "def": 283 } }, { @@ -3177,18 +3690,56 @@ "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-stm32-pybv11-stubs", "version": "1.21.0", "port": "stm32", - "module": 0.77, - "class": 0.68, - "def": 0.65, + "module": 0.78, + "class": 0.7, + "def": 0.68, "counts": { - "def": 1089, - "class": 117, - "module": 64 + "def": 1061, + "class": 113, + "module": 63 }, "missing": { - "module": 15, - "class": 37, - "def": 385 + "module": 14, + "class": 34, + "def": 343 + } + }, + { + "folder": "micropython-v1_21_0-stm32-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-stm32-stubs", + "version": "1.21.0", + "port": "stm32", + "module": 0.78, + "class": 0.7, + "def": 0.68, + "counts": { + "def": 1061, + "class": 113, + "module": 63 + }, + "missing": { + "module": 14, + "class": 34, + "def": 343 + } + }, + { + "folder": "micropython-v1_21_0-webassembly-stubs", + "path": "..\\repos\\micropython-stubs\\publish\\micropython-v1_21_0-webassembly-stubs", + "version": "1.21.0", + "port": "webassembly", + "module": 0.95, + "class": 0.7, + "def": 0.69, + "counts": { + "def": 316, + "class": 27, + "module": 40 + }, + "missing": { + "module": 2, + "class": 8, + "def": 98 } } ] \ No newline at end of file diff --git a/scripts/flash.ipynb b/scripts/flash.ipynb new file mode 100644 index 000000000..289e0ade6 --- /dev/null +++ b/scripts/flash.ipynb @@ -0,0 +1,520 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "import shutil, time\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "# Find firmware files for a given version port and board\n", + "# firmwares downloaded via get_firmware.ipynb are stored in firmware//-v[-preview.].\n", + "# NOTE: The build dates have been removed from the filenames as they mostly get in the way\n", + "\n", + "PORT_FWTYPES = {\n", + " \"stm32\": \".hex\",\n", + " \"esp32\": \".bin\",\n", + " \"rp2\": \".uf2\",\n", + " \"samd\": \".uf2\",\n", + "}\n", + "\n", + "\n", + "def find_firmware(version: str, *, board: str = \"*\", port: str):\n", + " fw_folder = Path.cwd() / \"firmware\" / port\n", + " if not fw_folder.exists():\n", + " print(f\"Folder {fw_folder} does not exist\")\n", + " return None\n", + " v_str = f\"v{version}\"\n", + " ext = PORT_FWTYPES[port]\n", + " if port == \"rp2\":\n", + " # PICO boards were renamed to RPI_PICO starting @v1.22.0 for all versions on the download page\n", + " if board.startswith(\"PICO\"):\n", + " board = f\"RPI_{board}\"\n", + " search = f\"{board}-{v_str}{ext}\"\n", + " elif port == \"esp32\":\n", + " board = board.replace(\"_OTA\", \"-OTA\")\n", + " search = f\"ESP32_{board}-{v_str}{ext}\"\n", + " else:\n", + " search = f\"{board}-{v_str}{ext}\"\n", + "\n", + " print(f\"Searching for {search} in {fw_folder}\")\n", + "\n", + " fw_files = list(fw_folder.glob(search))\n", + "\n", + " if not fw_files:\n", + " print(f\"No firmware files found for {v_str}\")\n", + " return None\n", + " fw_files.sort()\n", + " return fw_files" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# resolve board descriptions to BOARD_NAMES\n", + "import gc\n", + "\n", + "\n", + "import os\n", + "\n", + "\n", + "LIBS = [\"../src/stubber/data\"]\n", + "\n", + "\n", + "def file_exists(filename: str):\n", + " try:\n", + " if os.stat(filename)[0] >> 14:\n", + " return True\n", + "\n", + " return False\n", + "\n", + " except OSError:\n", + " return False\n", + "\n", + "\n", + "def read_boardname(info, desc: str = \"\"):\n", + " found = False\n", + "\n", + " for filename in [d + \"/board_info.csv\" for d in LIBS]:\n", + " # print(\"look up the board name in the file\", filename)\n", + "\n", + " if file_exists(filename):\n", + " descr = desc or info[\"board\"].strip()\n", + "\n", + " print(\"searching info file: {} for: '{}' \".format(filename, descr))\n", + "\n", + " if find_board(info, descr, filename):\n", + " found = True\n", + "\n", + " break\n", + " if not found:\n", + " print(\"Board not found, guessing board name\")\n", + "\n", + " descr = desc or info[\"board\"].strip()\n", + "\n", + " if \"with \" + info[\"cpu\"].upper() in descr:\n", + " # remove the with cpu part\n", + "\n", + " descr = descr.split(\"with \" + info[\"cpu\"].upper())[0].strip()\n", + "\n", + " info[\"board\"] = descr\n", + "\n", + " info[\"board\"] = info[\"board\"].replace(\" \", \"_\")\n", + "\n", + " gc.collect()\n", + "\n", + "\n", + "def find_board(info: dict, descr: str, filename: str):\n", + " # find a board based on its description\n", + "\n", + " short_hit = \"\"\n", + "\n", + " lines = []\n", + "\n", + " pos = descr.rfind(\" with\")\n", + "\n", + " if pos != -1:\n", + " short_descr = descr[:pos].strip()\n", + " else:\n", + " short_descr = \"\"\n", + "\n", + " try:\n", + " with open(filename, \"r\") as file:\n", + " lines = file.readlines()\n", + " for line in lines:\n", + " line = line.strip()\n", + "\n", + " if line.startswith(\"#\"):\n", + " continue\n", + "\n", + " _descr = line.split(\",\")[0].strip()\n", + "\n", + " _board = line.split(\",\")[1].strip()\n", + "\n", + " if _descr == descr:\n", + " info[\"board\"] = _board\n", + "\n", + " return True\n", + "\n", + " elif short_descr and _descr == short_descr:\n", + " if \"with\" in short_descr:\n", + " # Good enough - no need to trawl the entire file\n", + "\n", + " info[\"board\"] = _board\n", + "\n", + " return True\n", + "\n", + " # good enough if not found in the rest of the file (but slow)\n", + "\n", + " short_hit = _board\n", + "\n", + " if short_hit:\n", + " info[\"board\"] = short_hit\n", + "\n", + " return True\n", + "\n", + " return False\n", + "\n", + " finally:\n", + " del lines\n", + "\n", + " gc.collect()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "# flash .UF2 devices via bootloader and filecopy\n", + "from typing import Dict, Optional\n", + "import psutil\n", + "MCUInfo = Dict[str,str]\n", + "def flash_uf2(mcu:MCUInfo, fw_file:Optional[Path]= None) -> Optional[MCUInfo]:\n", + " if not PORT_FWTYPES[mcu[\"port\"]] in [\".uf2\"]:\n", + " print(f\"UF2 not supported on {mcu['board']} on {mcu['serial_port']}\")\n", + " return None\n", + "\n", + " print(f\"Entering UF2 bootloader on {mcu['board']} on {mcu['serial_port']}\")\n", + " %mpy --select {mcu['serial_port']}\n", + " # %mpy --bootloader # same as mpremote bootloader, for the current active device\n", + " !mpremote connect {mcu['serial_port']} bootloader\n", + "\n", + " destination = \"\"\n", + " wait = 5\n", + " while not destination and wait > 0:\n", + " print(f\"Waiting for mcu to mount as a drive : {wait} seconds left\")\n", + " drives = [drive.device for drive in psutil.disk_partitions()]\n", + " for drive in drives:\n", + " if Path(drive, \"INFO_UF2.TXT\").exists():\n", + " # Option : read Board-ID from INFO_UF2.TXT\n", + " board_id = \"Unknown\"\n", + " with open(Path(drive, \"INFO_UF2.TXT\")) as f:\n", + " data = f.readlines()\n", + " for line in data:\n", + " if line.startswith(\"Board-ID=\"):\n", + " board_id = line.split(\"=\")[1].strip()\n", + " print(f\"Found Board-ID={board_id}\")\n", + " destination = drive\n", + " break\n", + " time.sleep(1)\n", + " wait -= 1\n", + " if not destination or not Path(destination).exists() or not Path(destination, \"INFO_UF2.TXT\").exists():\n", + " print(\"Board is not in bootloader mode\")\n", + " return None\n", + " else:\n", + " print(\"Board is in bootloader mode\")\n", + " fw_file = fw_file or find_firmware(target_version, port=mcu[\"port\"], board=mcu[\"board\"]).__next__()\n", + " print(f\"Copying {fw_file} to {destination}\")\n", + " shutil.copy(fw_file, destination)\n", + " print(\"Done copying, resetting the board and wait for it to restart\")\n", + " time.sleep(5)\n", + " new_info:MCUInfo = %mpy --select {mcu['serial_port']} --info\n", + " return new_info" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "# Flash ESP32 via esptool\n", + "def flash_esp32(mcu:MCUInfo, fw_file:Optional[Path]= None,*, erase_flash:bool=True) -> Optional[MCUInfo]:\n", + " if not mcu[\"port\"] in [\"esp32\"]:\n", + " print(f\"ESP32 not supported on {mcu['board']} on {mcu['serial_port']}\")\n", + " return None\n", + "\n", + " fw_file = fw_file or find_firmware(target_version, port=mcu[\"port\"], board=mcu[\"board\"]).__next__()\n", + " print(f\"Flashing {fw_file} on {mcu['board']} on {mcu['serial_port']}\")\n", + " baud_rate = str(2_000_000)\n", + " baud_rate = str(512_000)\n", + " if mcu[\"cpu\"].upper()== \"ESP32\":\n", + " if erase_flash:\n", + " !esptool --chip esp32 --port {mcu[\"serial_port\"]} erase_flash\n", + " !esptool --chip esp32 --port {mcu[\"serial_port\"]} -b {baud_rate} write_flash -z 0x1000 {fw_file}\n", + " elif mcu[\"cpu\"].upper() == \"ESP32S3\":\n", + " if erase_flash:\n", + " !esptool --chip esp32s3 --port {mcu[\"serial_port\"]} erase_flash\n", + " !esptool --chip esp32s3 --port {mcu[\"serial_port\"]} -b {baud_rate} write_flash -z 0x0 {fw_file}\n", + "\n", + " print(\"Done flashing, resetting the board and wait for it to restart\")\n", + " time.sleep(5)\n", + " new_info:MCUInfo = %mpy --select {mcu['serial_port']} --info\n", + " return new_info\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "# flash STM32 using STM32CubeProgrammer\n", + "import bincopy \n", + "\n", + "def get_stm32_start_address(fw_file):\n", + " \"\"\"\n", + " Get the start address of the firmware file indoer to allow automatic restart after flashing\n", + " \"\"\"\n", + " try:\n", + " hex = bincopy.BinFile(str(fw_file))\n", + " return f\"0x{hex.execution_start_address:08X}\"\n", + " except:\n", + " return \"\"\n", + "\n", + "def flash_stm32(mcu:MCUInfo, fw_file:Optional[Path]= None,*, erase_flash:bool=True) -> Optional[MCUInfo]:\n", + " print(f\"Entering STM bootloader on {mcu['board']} on {mcu['serial_port']}\")\n", + " %mpy --select {mcu['serial_port']}\n", + " %mpy --bootloader # TODO: add this to micropython-magic\n", + "\n", + " time.sleep(2)\n", + " results = !\"C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe\" --list\n", + " if \"Product ID : STM32 BOOTLOADER\" in results:\n", + " print(\"No STM32 BOOTLOADER detected\")\n", + " return None\n", + " echo = False\n", + " for line in results:\n", + " if line.startswith(\"===== DFU Interface\"):\n", + " echo = True\n", + " if line.startswith(\"===== STLink\"):\n", + " echo = False\n", + " if echo:\n", + " print(line)\n", + "\n", + " # !\"C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe\" --connect port=USB1\n", + " if erase_flash:\n", + " print(\"Erasing flash\")\n", + " !\"C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe\" --connect port=USB1 --erase all\n", + " \n", + " print(\"Flashing\")\n", + " fw_file = fw_file or find_firmware(target_version, port=mcu[\"port\"], board=mcu[\"board\"]).__next__()\n", + " start_address = get_stm32_start_address(fw_file)\n", + "\n", + " print(f\"STM32_Programmer_CLI.exe --connect port=USB1 --write {str(fw_file)} --go {start_address}\")\n", + " !\"C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe\" --connect port=USB1 --write \"{str(fw_file)}\" --go {start_address}\n", + " print(\"Done flashing, resetting the board and wait for it to restart\")\n", + " time.sleep(5)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "searching info file: ../src/stubber/data/board_info.csv for: 'Wio Terminal D51R with SAMD51P19A' \n", + "| ver | version | port | mpy | build | cpu | family | board | arch | serial_port |\n", + "|---------|-----------|--------|-------|---------|------------|-------------|--------------------|-----------|---------------|\n", + "| v1.22.0 | 1.22.0 | samd | v6.2 | | SAMD51P19A | micropython | SEEED_WIO_TERMINAL | armv7emsp | COM3 |\n" + ] + } + ], + "source": [ + "def get_dev_info():\n", + " devs = %mpy --list\n", + " ports = devs.fields(0) # list of ports\n", + " # build a list of dicts with info for each port and attached board \n", + " dev_info = []\n", + " for p in ports:\n", + " info = %mpy --select {p} --info\n", + " if info:\n", + " try:\n", + " info[\"version\"] = info[\"version\"].rstrip(\".\")\n", + " read_boardname(info)\n", + " finally:\n", + " dev_info.append(info)\n", + " return dev_info\n", + "\n", + "def version_str(version: tuple): # -> str:\n", + " \"\"\"Convert a micropython version tuple to a string\"\"\"\n", + " v_str = \".\".join([str(n) for n in version[:3]])\n", + " if len(version) > 3 and version[3]:\n", + " v_str += \"-\" + version[3]\n", + " return v_str\n", + "\n", + "dev_info = get_dev_info()\n", + "from tabulate import tabulate\n", + "print(tabulate(dev_info, headers=\"keys\", tablefmt=\"github\"))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Searching for SEEED_WIO_TERMINAL-v1.22.1.uf2 in c:\\develop\\MyPython\\micropython-stubber\\scripts\\firmware\\samd\n", + "c:\\develop\\MyPython\\micropython-stubber\\scripts\\firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.1.uf2 {'ver': 'v1.22.0', 'version': '1.22.0', 'port': 'samd', 'mpy': 'v6.2', 'build': '', 'cpu': 'SAMD51P19A', 'family': 'micropython', 'board': 'SEEED_WIO_TERMINAL', 'arch': 'armv7emsp', 'serial_port': 'COM3'}\n", + "Entering UF2 bootloader on SEEED_WIO_TERMINAL on COM3\n", + "Waiting for mcu to mount as a drive : 5 seconds left\n", + "Waiting for mcu to mount as a drive : 4 seconds left\n", + "Found Board-ID=Unknown\n", + "Board is in bootloader mode\n", + "Copying c:\\develop\\MyPython\\micropython-stubber\\scripts\\firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.1.uf2 to F:\\\n", + "Done copying, resetting the board and wait for it to restart\n", + "searching info file: ../src/stubber/data/board_info.csv for: 'Wio Terminal D51R with SAMD51P19A' \n", + "| ver | version | port | mpy | build | cpu | family | board | arch | serial_port |\n", + "|---------|-----------|--------|-------|---------|------------|-------------|--------------------|-----------|---------------|\n", + "| v1.22.1 | 1.22.1 | samd | v6.2 | | SAMD51P19A | micropython | SEEED_WIO_TERMINAL | armv7emsp | COM3 |\n" + ] + } + ], + "source": [ + "target_version = \"1.23.0-preview.*\"\n", + "target_version = \"1.22.1\"\n", + "updated = []\n", + "\n", + "\n", + "\n", + "for mcu in dev_info:\n", + " firmwares = find_firmware(target_version, port=mcu[\"port\"], board=mcu[\"board\"])\n", + " if not firmwares:\n", + " firmwares = find_firmware(target_version, port=mcu[\"port\"], board=mcu[\"board\"].split(\"_\")[0])\n", + " if not firmwares:\n", + " print(f\"No firmware found for {mcu['board']} on {mcu['serial_port']}\")\n", + "\n", + " continue\n", + " fw_file = firmwares[0]\n", + "\n", + " print(fw_file, mcu)\n", + " new = None\n", + " try:\n", + " if mcu[\"port\"] in [\"samd\", \"rp2\"]:\n", + " new = flash_uf2(mcu, fw_file=fw_file)\n", + " elif mcu[\"port\"] in [\"esp32\"]:\n", + " new = flash_esp32(mcu, erase_flash=False, fw_file=fw_file)\n", + "\n", + "\n", + " elif mcu[\"port\"] in [\"stm32\"]:\n", + " new = flash_stm32(mcu, erase_flash=False, fw_file=fw_file)\n", + "\n", + " if new:\n", + " read_boardname(new)\n", + "\n", + "\n", + " new[\"version\"] = new[\"version\"].rstrip(\".\") # todo: update micropython-magic to do this\n", + "\n", + "\n", + " updated.append(new)\n", + " except Exception as e:\n", + " print(\"Failed to flash\", mcu[\"board\"], mcu[\"serial_port\"])\n", + " print(e)\n", + "\n", + "\n", + "\n", + "print(tabulate(updated, headers=\"keys\", tablefmt=\"github\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'ver': 'v1.22.1',\n", + " 'version': '1.22.1',\n", + " 'port': 'samd',\n", + " 'mpy': 'v6.2',\n", + " 'build': '',\n", + " 'cpu': 'SAMD51P19A',\n", + " 'family': 'micropython',\n", + " 'board': 'Wio Terminal D51R with SAMD51P19A',\n", + " 'arch': 'armv7emsp',\n", + " 'serial_port': 'COM3'}" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "%mpy --info" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(name='micropython', version=(1, 22, 1, ''), _machine='Wio Terminal D51R with SAMD51P19A', _mpy=7686)\n" + ] + } + ], + "source": [ + "# %%micropython\n", + "\n", + "import sys\n", + "\n", + "print(sys.implementation)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "namespace(name='cpython', cache_tag='cpython-311', version=sys.version_info(major=3, minor=11, micro=7, releaselevel='final', serial=0), hexversion=51054576)\n" + ] + } + ], + "source": [ + "import sys\n", + "\n", + "print(sys.implementation)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/scripts/get-boardnames.py b/scripts/get-boardnames.py deleted file mode 100644 index 4675b5bd0..000000000 --- a/scripts/get-boardnames.py +++ /dev/null @@ -1,73 +0,0 @@ -from dataclasses import dataclass, is_dataclass, asdict - -from pathlib import Path -import re -from typing import List -from tabulate import tabulate -import json - - -@dataclass -class Board: - """MicroPython Board definition""" - - port: str - board: str - board_name: str - mcu_name: str - description: str - path: Path - - -# look for all mpconfigboard.h files and extract the board name -# from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6" -# and the #define MICROPY_HW_MCU_NAME "STM32F767xx" - -mpy_path = Path("repos/micropython") -re_board_name = re.compile(r"#define MICROPY_HW_BOARD_NAME\s+\"(.+)\"") -re_mcu_name = re.compile(r"#define MICROPY_HW_MCU_NAME\s+\"(.+)\"") - - -board_list: List[Board] = [] - -for path in mpy_path.glob("**/mpconfigboard.h"): - board = path.parent.name - port = path.parent.parent.parent.name - with open(path, "r") as f: - board_name = mcu_name = "-" - found = 0 - for line in f: - if match := re_board_name.match(line): - board_name = match[1] - found += 1 - elif match := re_mcu_name.match(line): - mcu_name = match[1] - found += 1 - if found == 2: - break - description = f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name - board_list.append(Board(port, board, board_name, mcu_name, description, path)) - -print(f"Found {len(board_list)} board definitions.") - -print(tabulate(board_list, headers="keys")) # type: ignore - - -class EnhancedJSONEncoder(json.JSONEncoder): - def default(self, o): - if is_dataclass(o): - return asdict(o) - elif isinstance(o, Path): - return o.as_posix() - return super().default(o) - - -# write the list to json file -with open("src/stubber/data/board_info.json", "w") as f: - json.dump(board_list, f, indent=4, cls=EnhancedJSONEncoder) - -# create a csv with only the board and the description of the board_list -with open("src/stubber/data/board_info.csv", "w") as f: - f.write("board,description\n") - for board in board_list: - f.write(f"{board.description},{board.board}\n") diff --git a/scripts/get_boardnames.py b/scripts/get_boardnames.py new file mode 100644 index 000000000..ce1272268 --- /dev/null +++ b/scripts/get_boardnames.py @@ -0,0 +1,215 @@ +""" +Collects board name and description information from MicroPython and writes it to JSON and CSV files. +""" + +import json +import re +from dataclasses import asdict, dataclass, is_dataclass +from pathlib import Path +from typing import List + +from tabulate import tabulate + +import stubber.basicgit as git + + +@dataclass(kw_only=True) +class Board: + """MicroPython Board definition""" + + description: str + port: str + board: str + board_name: str + mcu_name: str + path: Path + version: str = "" + + +class EnhancedJSONEncoder(json.JSONEncoder): + def default(self, o: object): + if is_dataclass(o): + return asdict(o) + elif isinstance(o, Path): + return o.as_posix() + return super().default(o) + + +# look for all mpconfigboard.h files and extract the board name +# from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6" +# and the #define MICROPY_HW_MCU_NAME "STM32F767xx" + + +RE_BOARD_NAME = re.compile(r"#define\s+MICROPY_HW_BOARD_NAME\s+\"(.+)\"") +RE_MCU_NAME = re.compile(r"#define\s+MICROPY_HW_MCU_NAME\s+\"(.+)\"") +RE_CMAKE_BOARD_NAME = re.compile(r"MICROPY_HW_BOARD_NAME\s?=\s?\"(?P[\w\s\S]*)\"") +RE_CMAKE_MCU_NAME = re.compile(r"MICROPY_HW_MCU_NAME\s?=\s?\"(?P[\w\s\S]*)\"") +# TODO: normal make files + + +def collect_boardinfo(mpy_path: Path, version: str) -> List[Board]: + """Collects board name and decriptions from mpconfigboard.h files. + + Args: + mpy_path (Path): The path to the MicroPython repository. + version (str): The version of MicroPython. + + Returns: + List[Board]: A list of Board objects containing the board information. + """ + board_list: List[Board] = [] + # look in boards + for path in mpy_path.glob("ports/**/mpconfigboard.h"): + board = path.parent.name + port = path.parent.parent.parent.name + with open(path, "r") as f: + board_name = mcu_name = "-" + found = 0 + for line in f: + if match := RE_BOARD_NAME.match(line): + board_name = match[1] + found += 1 + elif match := RE_MCU_NAME.match(line): + mcu_name = match[1] + found += 1 + if found == 2: + description = ( + f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name + ) + board_list.append( + Board( + port=port, + board=board, + board_name=board_name, + mcu_name=mcu_name, + description=description, + path=path.relative_to(mpy_path), + version=version, + ) + ) + found = 0 + if found == 1: + description = board_name + board_list.append( + Board( + port=port, + board=board, + board_name=board_name, + mcu_name=mcu_name, + description=description, + path=path.relative_to(mpy_path), + version=version, + ) + ) + # look for variants in the .cmake files + for path in mpy_path.glob("ports/**/mpconfigboard.cmake"): + board = path.parent.name + port = path.parent.parent.parent.name + with open(path, "r") as f: + board_name = mcu_name = "-" + found = 0 + for line in f: + line = line.strip() + if match := RE_CMAKE_BOARD_NAME.match(line): + description = match["variant"] + board_list.append( + Board( + port=port, + board=board, + board_name=board_name, + mcu_name=mcu_name, + description=description, + path=path.relative_to(mpy_path), + version=version, + ) + ) + elif match := RE_CMAKE_MCU_NAME.match(line): + description = match["variant"] + board_list.append( + Board( + port=port, + board=board, + board_name=board_name, + mcu_name=mcu_name, + description=description, + path=path.relative_to(mpy_path), + version=version, + ) + ) + + # look for variants in the Makefile files + + return board_list + + +def write_files(board_list: List[Board], *, folder: Path): + """Writes the board information to JSON and CSV files. + + Args: + board_list (List[Board]): The list of Board objects. + """ + # write the list to json file + with open(folder / "board_info.json", "w") as f: + json.dump(board_list, f, indent=4, cls=EnhancedJSONEncoder) + + # create a csv with only the board and the description of the board_list + with open(folder / "board_info.csv", "w") as f: + f.write("board,description\n") + for board in board_list: + f.write(f"{board.description},{board.board}\n") + + +def get_board_list(versions: List[str], mpy_path: Path): + """Gets the list of boards for multiple versions of MicroPython. + + Args: + versions (List[str]): The list of MicroPython versions. + mpy_path (Path): The path to the MicroPython repository. + + Returns: + List[Board]: The list of Board objects. + """ + board_list: List[Board] = [] + for version in versions: + print(git.checkout_tag(tag=version, repo=mpy_path)) + new_ones = collect_boardinfo(mpy_path, version) + print(f"Found {len(new_ones)} board definitions for {version}.") + board_list += new_ones + + # sort the board_list by description and board + print("Total number of boards found:", len(board_list)) + seen = set() + board_list = [x for x in board_list if not (x.description in seen or seen.add(x.description))] + board_list.sort(key=lambda x: x.description.lower()) + print("Unique board descriptions found:", len(board_list)) + return board_list + + +def main(): + """Main function to collect and write board information.""" + mpy_path = Path("repos/micropython") + versions = [ + "v1.22.0", + "v1.21.0", + "v1.20.0", + "v1.19.1", + "v1.18", + "v1.17", + "v1.16", + "v1.15", + "v1.14", + "v1.13", + "v1.12", + "v1.11", + "v1.10", + ] + # versions.reverse() + board_list = get_board_list(versions, mpy_path) + + print(tabulate(board_list, headers="keys")) # type: ignore + write_files(board_list, folder=Path("src/stubber/data")) + write_files(board_list, folder=Path("src/stubber/board")) + + +if __name__ == "__main__": + main() diff --git a/scripts/get_firmwares.ipynb b/scripts/get_firmwares.ipynb index b70a0e7e4..3316143c8 100644 --- a/scripts/get_firmwares.ipynb +++ b/scripts/get_firmwares.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -21,7 +21,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": { "tags": [] }, @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": { "tags": [] }, @@ -68,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -86,13 +86,13 @@ " )\n", " if \"?\" in base_url:\n", " base_url = base_url.split(\"?\")[0]\n", - " links = [urljoin(base_url, tag.get(\"href\")) for tag in tags]\n", + " links: List = [urljoin(base_url, tag.get(\"href\")) for tag in tags]\n", " return links" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": { "tags": [] }, @@ -101,7 +101,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Found 138 firmwares\n" + "Found 186 firmwares\n" ] } ], @@ -118,6 +118,7 @@ "RELEVANT_BOARDS = [\n", " \"PYBV11\",\n", " \"ESP32_GENERIC\",\n", + " \"ESP32_GENERIC_S3\",\n", " \"RPI_PICO\",\n", " \"RPI_PICO_W\",\n", " \"ARDUINO_NANO_RP2040_CONNECT\",\n", @@ -165,14 +166,14 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Found 75 relevant firmwares\n" + "Found 102 relevant firmwares\n" ] } ], @@ -180,97 +181,109 @@ "relevant = [\n", " board\n", " for board in board_urls\n", - " if board[\"board\"] in RELEVANT_BOARDS and (board[\"version\"] in [\"1.21.0\"] or board[\"preview\"])\n", + " if board[\"board\"] in RELEVANT_BOARDS and (board[\"version\"] in [\"1.22.0\", \"1.22.1\"] or board[\"preview\"])\n", " # and b[\"port\"] in [\"esp32\", \"rp2\"]\n", "]\n", + "\n", "# relevant\n", + "\n", "print(f\"Found {len(relevant)} relevant firmwares\")" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Downloading https://micropython.org/resources/firmware/PYBV11-20231005-v1.21.0.hex to firmware\\stm32\\PYBV11-v1.21.0.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-20231019-v1.22.0-preview.32.g86c7b957a.hex to firmware\\stm32\\PYBV11-v1.22.0-preview.32.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-20231017-v1.22.0-preview.31.g3883f2948.hex to firmware\\stm32\\PYBV11-v1.22.0-preview.31.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-20231017-v1.22.0-preview.30.ge78471416.hex to firmware\\stm32\\PYBV11-v1.22.0-preview.30.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-20231017-v1.22.0-preview.27.gc2361328e.hex to firmware\\stm32\\PYBV11-v1.22.0-preview.27.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20231005-v1.21.0.hex to firmware\\stm32\\PYBV11-DP-v1.21.0.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20231019-v1.22.0-preview.32.g86c7b957a.hex to firmware\\stm32\\PYBV11-DP-v1.22.0-preview.32.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20231017-v1.22.0-preview.31.g3883f2948.hex to firmware\\stm32\\PYBV11-DP-v1.22.0-preview.31.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20231017-v1.22.0-preview.30.ge78471416.hex to firmware\\stm32\\PYBV11-DP-v1.22.0-preview.30.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20231017-v1.22.0-preview.27.gc2361328e.hex to firmware\\stm32\\PYBV11-DP-v1.22.0-preview.27.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20231005-v1.21.0.hex to firmware\\stm32\\PYBV11-NETWORK-v1.21.0.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20231019-v1.22.0-preview.32.g86c7b957a.hex to firmware\\stm32\\PYBV11-NETWORK-v1.22.0-preview.32.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20231017-v1.22.0-preview.31.g3883f2948.hex to firmware\\stm32\\PYBV11-NETWORK-v1.22.0-preview.31.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20231017-v1.22.0-preview.30.ge78471416.hex to firmware\\stm32\\PYBV11-NETWORK-v1.22.0-preview.30.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20231017-v1.22.0-preview.27.gc2361328e.hex to firmware\\stm32\\PYBV11-NETWORK-v1.22.0-preview.27.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20231005-v1.21.0.hex to firmware\\stm32\\PYBV11-THREAD-v1.21.0.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20231019-v1.22.0-preview.32.g86c7b957a.hex to firmware\\stm32\\PYBV11-THREAD-v1.22.0-preview.32.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20231017-v1.22.0-preview.31.g3883f2948.hex to firmware\\stm32\\PYBV11-THREAD-v1.22.0-preview.31.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20231017-v1.22.0-preview.30.ge78471416.hex to firmware\\stm32\\PYBV11-THREAD-v1.22.0-preview.30.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20231017-v1.22.0-preview.27.gc2361328e.hex to firmware\\stm32\\PYBV11-THREAD-v1.22.0-preview.27.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20231005-v1.21.0.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.21.0.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20231019-v1.22.0-preview.32.g86c7b957a.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.22.0-preview.32.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20231017-v1.22.0-preview.31.g3883f2948.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.22.0-preview.31.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20231017-v1.22.0-preview.30.ge78471416.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.22.0-preview.30.hex\n", - "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20231017-v1.22.0-preview.27.gc2361328e.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.22.0-preview.27.hex\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20231005-v1.21.0.bin to firmware\\esp32\\ESP32_GENERIC-v1.21.0.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20231019-v1.22.0-preview.32.g86c7b957a.bin to firmware\\esp32\\ESP32_GENERIC-v1.22.0-preview.32.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20231017-v1.22.0-preview.31.g3883f2948.bin to firmware\\esp32\\ESP32_GENERIC-v1.22.0-preview.31.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20231017-v1.22.0-preview.30.ge78471416.bin to firmware\\esp32\\ESP32_GENERIC-v1.22.0-preview.30.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20231017-v1.22.0-preview.27.gc2361328e.bin to firmware\\esp32\\ESP32_GENERIC-v1.22.0-preview.27.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20231005-v1.21.0.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.21.0.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20231019-v1.22.0-preview.32.g86c7b957a.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.22.0-preview.32.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20231017-v1.22.0-preview.31.g3883f2948.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.22.0-preview.31.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20231017-v1.22.0-preview.30.ge78471416.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.22.0-preview.30.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20231017-v1.22.0-preview.27.gc2361328e.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.22.0-preview.27.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20231005-v1.21.0.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.21.0.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20231019-v1.22.0-preview.32.g86c7b957a.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.22.0-preview.32.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20231017-v1.22.0-preview.31.g3883f2948.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.22.0-preview.31.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20231017-v1.22.0-preview.30.ge78471416.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.22.0-preview.30.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20231017-v1.22.0-preview.27.gc2361328e.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.22.0-preview.27.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20231005-v1.21.0.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.21.0.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20231019-v1.22.0-preview.32.g86c7b957a.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.22.0-preview.32.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20231017-v1.22.0-preview.31.g3883f2948.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.22.0-preview.31.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20231017-v1.22.0-preview.30.ge78471416.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.22.0-preview.30.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20231017-v1.22.0-preview.27.gc2361328e.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.22.0-preview.27.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20231005-v1.21.0.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.21.0.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20231019-v1.22.0-preview.32.g86c7b957a.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.22.0-preview.32.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20231017-v1.22.0-preview.31.g3883f2948.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.22.0-preview.31.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20231017-v1.22.0-preview.30.ge78471416.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.22.0-preview.30.bin\n", - "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20231017-v1.22.0-preview.27.gc2361328e.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.22.0-preview.27.bin\n", - "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20231005-v1.21.0.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.21.0.uf2\n", - "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20231019-v1.22.0-preview.32.g86c7b957a.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.22.0-preview.32.uf2\n", - "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20231017-v1.22.0-preview.31.g3883f2948.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.22.0-preview.31.uf2\n", - "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20231017-v1.22.0-preview.30.ge78471416.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.22.0-preview.30.uf2\n", - "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20231017-v1.22.0-preview.27.gc2361328e.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.22.0-preview.27.uf2\n", - "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20231005-v1.21.0.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.21.0.uf2\n", - "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20231019-v1.22.0-preview.32.g86c7b957a.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.22.0-preview.32.uf2\n", - "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20231017-v1.22.0-preview.31.g3883f2948.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.22.0-preview.31.uf2\n", - "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20231017-v1.22.0-preview.30.ge78471416.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.22.0-preview.30.uf2\n", - "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20231017-v1.22.0-preview.27.gc2361328e.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.22.0-preview.27.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO-20231005-v1.21.0.uf2 to firmware\\rp2\\RPI_PICO-v1.21.0.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO-20231019-v1.22.0-preview.32.g86c7b957a.uf2 to firmware\\rp2\\RPI_PICO-v1.22.0-preview.32.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO-20231017-v1.22.0-preview.31.g3883f2948.uf2 to firmware\\rp2\\RPI_PICO-v1.22.0-preview.31.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO-20231017-v1.22.0-preview.30.ge78471416.uf2 to firmware\\rp2\\RPI_PICO-v1.22.0-preview.30.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO-20231017-v1.22.0-preview.27.gc2361328e.uf2 to firmware\\rp2\\RPI_PICO-v1.22.0-preview.27.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20231005-v1.21.0.uf2 to firmware\\rp2\\RPI_PICO_W-v1.21.0.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20231019-v1.22.0-preview.32.g86c7b957a.uf2 to firmware\\rp2\\RPI_PICO_W-v1.22.0-preview.32.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20231017-v1.22.0-preview.31.g3883f2948.uf2 to firmware\\rp2\\RPI_PICO_W-v1.22.0-preview.31.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20231017-v1.22.0-preview.30.ge78471416.uf2 to firmware\\rp2\\RPI_PICO_W-v1.22.0-preview.30.uf2\n", - "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20231017-v1.22.0-preview.27.gc2361328e.uf2 to firmware\\rp2\\RPI_PICO_W-v1.22.0-preview.27.uf2\n", - "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20231005-v1.21.0.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.21.0.uf2\n", - "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20231019-v1.22.0-preview.32.g86c7b957a.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.0-preview.32.uf2\n", - "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20231017-v1.22.0-preview.31.g3883f2948.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.0-preview.31.uf2\n", - "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20231017-v1.22.0-preview.30.ge78471416.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.0-preview.30.uf2\n", - "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20231017-v1.22.0-preview.27.gc2361328e.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.0-preview.27.uf2\n" + "Downloading https://micropython.org/resources/firmware/PYBV11-20240105-v1.22.1.hex to firmware\\stm32\\PYBV11-v1.22.1.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-20240124-v1.23.0-preview.62.g6bb446b7f.hex to firmware\\stm32\\PYBV11-v1.23.0-preview.62.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-20240123-v1.23.0-preview.60.gce2058685.hex to firmware\\stm32\\PYBV11-v1.23.0-preview.60.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-20240123-v1.23.0-preview.59.g057701a77.hex to firmware\\stm32\\PYBV11-v1.23.0-preview.59.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-20240123-v1.23.0-preview.58.gc3ca3612d.hex to firmware\\stm32\\PYBV11-v1.23.0-preview.58.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20240105-v1.22.1.hex to firmware\\stm32\\PYBV11-DP-v1.22.1.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20240124-v1.23.0-preview.62.g6bb446b7f.hex to firmware\\stm32\\PYBV11-DP-v1.23.0-preview.62.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20240123-v1.23.0-preview.60.gce2058685.hex to firmware\\stm32\\PYBV11-DP-v1.23.0-preview.60.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20240123-v1.23.0-preview.59.g057701a77.hex to firmware\\stm32\\PYBV11-DP-v1.23.0-preview.59.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP-20240123-v1.23.0-preview.58.gc3ca3612d.hex to firmware\\stm32\\PYBV11-DP-v1.23.0-preview.58.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20240105-v1.22.1.hex to firmware\\stm32\\PYBV11-NETWORK-v1.22.1.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20240124-v1.23.0-preview.62.g6bb446b7f.hex to firmware\\stm32\\PYBV11-NETWORK-v1.23.0-preview.62.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20240123-v1.23.0-preview.60.gce2058685.hex to firmware\\stm32\\PYBV11-NETWORK-v1.23.0-preview.60.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20240123-v1.23.0-preview.59.g057701a77.hex to firmware\\stm32\\PYBV11-NETWORK-v1.23.0-preview.59.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-NETWORK-20240123-v1.23.0-preview.58.gc3ca3612d.hex to firmware\\stm32\\PYBV11-NETWORK-v1.23.0-preview.58.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20240105-v1.22.1.hex to firmware\\stm32\\PYBV11-THREAD-v1.22.1.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20240124-v1.23.0-preview.62.g6bb446b7f.hex to firmware\\stm32\\PYBV11-THREAD-v1.23.0-preview.62.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20240123-v1.23.0-preview.60.gce2058685.hex to firmware\\stm32\\PYBV11-THREAD-v1.23.0-preview.60.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20240123-v1.23.0-preview.59.g057701a77.hex to firmware\\stm32\\PYBV11-THREAD-v1.23.0-preview.59.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-THREAD-20240123-v1.23.0-preview.58.gc3ca3612d.hex to firmware\\stm32\\PYBV11-THREAD-v1.23.0-preview.58.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20240105-v1.22.1.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.22.1.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20240124-v1.23.0-preview.62.g6bb446b7f.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.23.0-preview.62.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20240123-v1.23.0-preview.60.gce2058685.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.23.0-preview.60.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20240123-v1.23.0-preview.59.g057701a77.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.23.0-preview.59.hex\n", + "Downloading https://micropython.org/resources/firmware/PYBV11-DP_THREAD-20240123-v1.23.0-preview.58.gc3ca3612d.hex to firmware\\stm32\\PYBV11-DP_THREAD-v1.23.0-preview.58.hex\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-UNICORE-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC-UNICORE-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-OTA-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC-OTA-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-D2WD-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC-D2WD-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC-SPIRAM-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC_S3-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC_S3-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC_S3-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC_S3-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC_S3-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-SPIRAM_OCT-20240105-v1.22.1.bin to firmware\\esp32\\ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.1.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-SPIRAM_OCT-20240124-v1.23.0-preview.62.g6bb446b7f.bin to firmware\\esp32\\ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.62.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-SPIRAM_OCT-20240123-v1.23.0-preview.60.gce2058685.bin to firmware\\esp32\\ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.60.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-SPIRAM_OCT-20240123-v1.23.0-preview.59.g057701a77.bin to firmware\\esp32\\ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.59.bin\n", + "Downloading https://micropython.org/resources/firmware/ESP32_GENERIC_S3-SPIRAM_OCT-20240123-v1.23.0-preview.58.gc3ca3612d.bin to firmware\\esp32\\ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.58.bin\n", + "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20240105-v1.22.1.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.22.1.uf2\n", + "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20240124-v1.23.0-preview.62.g6bb446b7f.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.23.0-preview.62.uf2\n", + "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20240123-v1.23.0-preview.60.gce2058685.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.23.0-preview.60.uf2\n", + "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20240123-v1.23.0-preview.59.g057701a77.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.23.0-preview.59.uf2\n", + "Downloading https://micropython.org/resources/firmware/ARDUINO_NANO_RP2040_CONNECT-20240123-v1.23.0-preview.58.gc3ca3612d.uf2 to firmware\\rp2\\ARDUINO_NANO_RP2040_CONNECT-v1.23.0-preview.58.uf2\n", + "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20240105-v1.22.1.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.22.1.uf2\n", + "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20240124-v1.23.0-preview.62.g6bb446b7f.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.23.0-preview.62.uf2\n", + "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20240123-v1.23.0-preview.60.gce2058685.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.23.0-preview.60.uf2\n", + "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20240123-v1.23.0-preview.59.g057701a77.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.23.0-preview.59.uf2\n", + "Downloading https://micropython.org/resources/firmware/PIMORONI_PICOLIPO_16MB-20240123-v1.23.0-preview.58.gc3ca3612d.uf2 to firmware\\rp2\\PIMORONI_PICOLIPO_16MB-v1.23.0-preview.58.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO-20240105-v1.22.1.uf2 to firmware\\rp2\\RPI_PICO-v1.22.1.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO-20240124-v1.23.0-preview.62.g6bb446b7f.uf2 to firmware\\rp2\\RPI_PICO-v1.23.0-preview.62.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO-20240123-v1.23.0-preview.60.gce2058685.uf2 to firmware\\rp2\\RPI_PICO-v1.23.0-preview.60.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO-20240123-v1.23.0-preview.59.g057701a77.uf2 to firmware\\rp2\\RPI_PICO-v1.23.0-preview.59.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO-20240123-v1.23.0-preview.58.gc3ca3612d.uf2 to firmware\\rp2\\RPI_PICO-v1.23.0-preview.58.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20240105-v1.22.1.uf2 to firmware\\rp2\\RPI_PICO_W-v1.22.1.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20240124-v1.23.0-preview.62.g6bb446b7f.uf2 to firmware\\rp2\\RPI_PICO_W-v1.23.0-preview.62.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20240123-v1.23.0-preview.60.gce2058685.uf2 to firmware\\rp2\\RPI_PICO_W-v1.23.0-preview.60.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20240123-v1.23.0-preview.59.g057701a77.uf2 to firmware\\rp2\\RPI_PICO_W-v1.23.0-preview.59.uf2\n", + "Downloading https://micropython.org/resources/firmware/RPI_PICO_W-20240123-v1.23.0-preview.58.gc3ca3612d.uf2 to firmware\\rp2\\RPI_PICO_W-v1.23.0-preview.58.uf2\n", + "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20240105-v1.22.1.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.22.1.uf2\n", + "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20240124-v1.23.0-preview.62.g6bb446b7f.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.23.0-preview.62.uf2\n", + "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20240123-v1.23.0-preview.60.gce2058685.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.23.0-preview.60.uf2\n", + "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20240123-v1.23.0-preview.59.g057701a77.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.23.0-preview.59.uf2\n", + "Downloading https://micropython.org/resources/firmware/SEEED_WIO_TERMINAL-20240123-v1.23.0-preview.58.gc3ca3612d.uf2 to firmware\\samd\\SEEED_WIO_TERMINAL-v1.23.0-preview.58.uf2\n" ] } ], @@ -291,6 +304,9 @@ " fname = re.sub(hash_re, \".\", fname)\n", " filename = firmware_folder / board[\"port\"] / fname\n", " filename.parent.mkdir(exist_ok=True)\n", + " if filename.exists():\n", + " # print(f\" {filename} already exists, skip download\")\n", + " continue\n", " print(f\"Downloading {board['firmware']} to {filename}\")\n", " r = requests.get(board[\"firmware\"], allow_redirects=True)\n", " with open(filename, \"wb\") as f:\n", @@ -300,14 +316,149 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Volume in drive C has no label.\n", + " Volume Serial Number is 30ED-D319\n", + "\n", + " Directory of c:\\develop\\MyPython\\micropython-stubber\\scripts\n", + "\n", + "\n", + " Directory of c:\\develop\\MyPython\\micropython-stubber\\scripts\\firmware\\esp32\n", + "\n", + "25-01-2024 00:00 .\n", + "19-10-2023 14:24 ..\n", + "19-10-2023 14:45 1.494.672 ESP32_GENERIC-D2WD-v1.21.0.bin\n", + "19-10-2023 14:45 1.494.832 ESP32_GENERIC-D2WD-v1.22.0-preview.27.bin\n", + "30-12-2023 00:28 1.493.840 ESP32_GENERIC-D2WD-v1.22.0-preview.278.bin\n", + "30-12-2023 00:28 1.494.112 ESP32_GENERIC-D2WD-v1.22.0-preview.281.bin\n", + "30-12-2023 00:28 1.494.176 ESP32_GENERIC-D2WD-v1.22.0-preview.283.bin\n", + "30-12-2023 00:28 1.494.144 ESP32_GENERIC-D2WD-v1.22.0-preview.289.bin\n", + "19-10-2023 14:45 1.494.832 ESP32_GENERIC-D2WD-v1.22.0-preview.30.bin\n", + "19-10-2023 14:45 1.494.832 ESP32_GENERIC-D2WD-v1.22.0-preview.31.bin\n", + "19-10-2023 14:45 1.494.832 ESP32_GENERIC-D2WD-v1.22.0-preview.32.bin\n", + "30-12-2023 00:28 1.494.112 ESP32_GENERIC-D2WD-v1.22.0.bin\n", + "25-01-2024 00:00 1.494.112 ESP32_GENERIC-D2WD-v1.22.1.bin\n", + "25-01-2024 00:00 1.494.320 ESP32_GENERIC-D2WD-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.494.320 ESP32_GENERIC-D2WD-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.494.144 ESP32_GENERIC-D2WD-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.494.320 ESP32_GENERIC-D2WD-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.494.320 ESP32_GENERIC-D2WD-v1.23.0-preview.62.bin\n", + "19-10-2023 14:45 1.621.792 ESP32_GENERIC-OTA-v1.21.0.bin\n", + "19-10-2023 14:45 1.621.936 ESP32_GENERIC-OTA-v1.22.0-preview.27.bin\n", + "30-12-2023 00:28 1.544.848 ESP32_GENERIC-OTA-v1.22.0-preview.278.bin\n", + "30-12-2023 00:28 1.545.104 ESP32_GENERIC-OTA-v1.22.0-preview.281.bin\n", + "30-12-2023 00:28 1.545.152 ESP32_GENERIC-OTA-v1.22.0-preview.283.bin\n", + "30-12-2023 00:28 1.545.120 ESP32_GENERIC-OTA-v1.22.0-preview.289.bin\n", + "19-10-2023 14:45 1.621.936 ESP32_GENERIC-OTA-v1.22.0-preview.30.bin\n", + "19-10-2023 14:45 1.621.952 ESP32_GENERIC-OTA-v1.22.0-preview.31.bin\n", + "19-10-2023 14:45 1.621.952 ESP32_GENERIC-OTA-v1.22.0-preview.32.bin\n", + "30-12-2023 00:28 1.545.088 ESP32_GENERIC-OTA-v1.22.0.bin\n", + "25-01-2024 00:00 1.545.088 ESP32_GENERIC-OTA-v1.22.1.bin\n", + "25-01-2024 00:00 1.545.312 ESP32_GENERIC-OTA-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.545.312 ESP32_GENERIC-OTA-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.545.120 ESP32_GENERIC-OTA-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.545.312 ESP32_GENERIC-OTA-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.545.312 ESP32_GENERIC-OTA-v1.23.0-preview.62.bin\n", + "19-10-2023 14:45 1.601.072 ESP32_GENERIC-SPIRAM-v1.21.0.bin\n", + "19-10-2023 14:45 1.601.200 ESP32_GENERIC-SPIRAM-v1.22.0-preview.27.bin\n", + "30-12-2023 00:28 1.614.304 ESP32_GENERIC-SPIRAM-v1.22.0-preview.278.bin\n", + "30-12-2023 00:28 1.614.576 ESP32_GENERIC-SPIRAM-v1.22.0-preview.281.bin\n", + "30-12-2023 00:28 1.614.640 ESP32_GENERIC-SPIRAM-v1.22.0-preview.283.bin\n", + "30-12-2023 00:28 1.614.624 ESP32_GENERIC-SPIRAM-v1.22.0-preview.289.bin\n", + "19-10-2023 14:45 1.601.200 ESP32_GENERIC-SPIRAM-v1.22.0-preview.30.bin\n", + "19-10-2023 14:45 1.601.200 ESP32_GENERIC-SPIRAM-v1.22.0-preview.31.bin\n", + "19-10-2023 14:45 1.601.200 ESP32_GENERIC-SPIRAM-v1.22.0-preview.32.bin\n", + "30-12-2023 00:28 1.614.576 ESP32_GENERIC-SPIRAM-v1.22.0.bin\n", + "25-01-2024 00:00 1.614.576 ESP32_GENERIC-SPIRAM-v1.22.1.bin\n", + "25-01-2024 00:00 1.614.832 ESP32_GENERIC-SPIRAM-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.614.832 ESP32_GENERIC-SPIRAM-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.614.624 ESP32_GENERIC-SPIRAM-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.614.832 ESP32_GENERIC-SPIRAM-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.614.832 ESP32_GENERIC-SPIRAM-v1.23.0-preview.62.bin\n", + "19-10-2023 14:45 1.653.872 ESP32_GENERIC-UNICORE-v1.21.0.bin\n", + "19-10-2023 14:45 1.654.032 ESP32_GENERIC-UNICORE-v1.22.0-preview.27.bin\n", + "30-12-2023 00:28 1.728.880 ESP32_GENERIC-UNICORE-v1.22.0-preview.278.bin\n", + "30-12-2023 00:28 1.729.200 ESP32_GENERIC-UNICORE-v1.22.0-preview.281.bin\n", + "30-12-2023 00:28 1.729.248 ESP32_GENERIC-UNICORE-v1.22.0-preview.283.bin\n", + "30-12-2023 00:28 1.729.232 ESP32_GENERIC-UNICORE-v1.22.0-preview.289.bin\n", + "19-10-2023 14:45 1.654.032 ESP32_GENERIC-UNICORE-v1.22.0-preview.30.bin\n", + "19-10-2023 14:45 1.654.032 ESP32_GENERIC-UNICORE-v1.22.0-preview.31.bin\n", + "19-10-2023 14:45 1.654.032 ESP32_GENERIC-UNICORE-v1.22.0-preview.32.bin\n", + "30-12-2023 00:28 1.729.152 ESP32_GENERIC-UNICORE-v1.22.0.bin\n", + "25-01-2024 00:00 1.729.152 ESP32_GENERIC-UNICORE-v1.22.1.bin\n", + "25-01-2024 00:00 1.729.424 ESP32_GENERIC-UNICORE-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.729.424 ESP32_GENERIC-UNICORE-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.729.232 ESP32_GENERIC-UNICORE-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.729.424 ESP32_GENERIC-UNICORE-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.729.424 ESP32_GENERIC-UNICORE-v1.23.0-preview.62.bin\n", + "19-10-2023 14:45 1.661.872 ESP32_GENERIC-v1.21.0.bin\n", + "19-10-2023 14:45 1.662.032 ESP32_GENERIC-v1.22.0-preview.27.bin\n", + "30-12-2023 00:28 1.737.408 ESP32_GENERIC-v1.22.0-preview.278.bin\n", + "30-12-2023 00:28 1.737.696 ESP32_GENERIC-v1.22.0-preview.281.bin\n", + "30-12-2023 00:28 1.737.760 ESP32_GENERIC-v1.22.0-preview.283.bin\n", + "30-12-2023 00:28 1.737.728 ESP32_GENERIC-v1.22.0-preview.289.bin\n", + "19-10-2023 14:45 1.662.032 ESP32_GENERIC-v1.22.0-preview.30.bin\n", + "19-10-2023 14:45 1.662.032 ESP32_GENERIC-v1.22.0-preview.31.bin\n", + "19-10-2023 14:45 1.662.032 ESP32_GENERIC-v1.22.0-preview.32.bin\n", + "30-12-2023 00:28 1.737.664 ESP32_GENERIC-v1.22.0.bin\n", + "25-01-2024 00:00 1.737.664 ESP32_GENERIC-v1.22.1.bin\n", + "25-01-2024 00:00 1.737.904 ESP32_GENERIC-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.737.904 ESP32_GENERIC-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.737.728 ESP32_GENERIC-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.737.904 ESP32_GENERIC-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.737.904 ESP32_GENERIC-v1.23.0-preview.62.bin\n", + "03-01-2024 11:55 1.561.184 ESP32_GENERIC_S3-SPIRAM_OCT-v1.21.0.bin\n", + "30-12-2023 00:29 1.631.216 ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.0-preview.278.bin\n", + "30-12-2023 00:29 1.631.536 ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.0-preview.281.bin\n", + "30-12-2023 00:29 1.631.600 ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.0-preview.283.bin\n", + "30-12-2023 00:29 1.631.584 ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.0-preview.289.bin\n", + "30-12-2023 00:29 1.631.504 ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.0.bin\n", + "25-01-2024 00:00 1.631.504 ESP32_GENERIC_S3-SPIRAM_OCT-v1.22.1.bin\n", + "25-01-2024 00:00 1.631.728 ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.631.728 ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.631.584 ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.631.728 ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.631.728 ESP32_GENERIC_S3-SPIRAM_OCT-v1.23.0-preview.62.bin\n", + "03-01-2024 11:55 1.559.472 ESP32_GENERIC_S3-v1.21.0.bin\n", + "30-12-2023 00:29 1.627.952 ESP32_GENERIC_S3-v1.22.0-preview.278.bin\n", + "30-12-2023 00:29 1.628.272 ESP32_GENERIC_S3-v1.22.0-preview.281.bin\n", + "30-12-2023 00:29 1.628.320 ESP32_GENERIC_S3-v1.22.0-preview.283.bin\n", + "30-12-2023 00:28 1.628.320 ESP32_GENERIC_S3-v1.22.0-preview.289.bin\n", + "30-12-2023 00:28 1.628.240 ESP32_GENERIC_S3-v1.22.0.bin\n", + "25-01-2024 00:00 1.628.240 ESP32_GENERIC_S3-v1.22.1.bin\n", + "25-01-2024 00:00 1.628.496 ESP32_GENERIC_S3-v1.23.0-preview.58.bin\n", + "25-01-2024 00:00 1.628.496 ESP32_GENERIC_S3-v1.23.0-preview.59.bin\n", + "03-01-2024 11:55 1.628.304 ESP32_GENERIC_S3-v1.23.0-preview.6.bin\n", + "25-01-2024 00:00 1.628.496 ESP32_GENERIC_S3-v1.23.0-preview.60.bin\n", + "25-01-2024 00:00 1.628.496 ESP32_GENERIC_S3-v1.23.0-preview.62.bin\n", + " 104 File(s) 168.480.160 bytes\n", + " 2 Dir(s) 355.654.905.856 bytes free\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "File Not Found\n" + ] + } + ], "source": [ - "# serialport = \"COM9\"\n", - "# !esptool --chip esp32 --port {serialport} erase_flash\n", - "# !esptool --chip esp32 --port {serialport} --baud 460800 write_flash -z 0x1000 firmware/ESP32_GENERIC-SPIRAM-v1.21.0.bin" + "!dir -l firmware\\esp32" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -326,7 +477,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/scripts/quick_check_array.ipynb b/scripts/quick_check_array.ipynb deleted file mode 100644 index b1f66ddef..000000000 --- a/scripts/quick_check_array.ipynb +++ /dev/null @@ -1,198 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "quick check to help find if/where `class array(List)` is being properly generarated" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "from pathlib import Path" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "94\n" - ] - } - ], - "source": [ - "stub_path = Path(\"../repos/micropython-stubs/stubs\")\n", - "pub_path = Path(\"../repos/micropython-stubs/publish\")\n", - "\n", - "version = \"v1_19_1*\"\n", - "version = \"v1_20_0*\"\n", - "version = \"v1_*\"\n", - "files = list(stub_path.glob(f\"micropython-{version}-merged/array.py\"))\n", - "files += list(stub_path.glob(f\"micropython-{version}-merged/array.pyi\"))\n", - "files += list(stub_path.glob(f\"micropython-{version}-docstubs/array.pyi\"))\n", - "files += list(pub_path.glob(f\"micropython-{version}/array.pyi\"))\n", - "\n", - "files = sorted(files, key=lambda f: str(f.parent))\n", - "\n", - "print(len(files))\n", - "# files" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "publish/micropython-v1_17-esp32-stubs/array.pyi class array: \n", - "publish/micropython-v1_17-esp8266-stubs/array.pyi class array: \n", - "publish/micropython-v1_17-rp2-stubs/array.pyi class array: \n", - "publish/micropython-v1_17-stm32-stubs/array.pyi class array: \n", - "publish/micropython-v1_18-esp32-stubs/array.pyi class array: \n", - "publish/micropython-v1_18-esp8266-stubs/array.pyi class array: \n", - "publish/micropython-v1_18-rp2-stubs/array.pyi class array: \n", - "publish/micropython-v1_18-stm32-stubs/array.pyi class array: \n", - "publish/micropython-v1_19_1-esp32-generic_s3-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_19_1-esp32-generic_spiram-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_19_1-esp32-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_19_1-esp32-um_tinypico-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_19_1-esp8266-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_19_1-rp2-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_19_1-stm32-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_20_0-esp32-generic_ota-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_20_0-esp32-generic_s3-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_20_0-esp32-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_20_0-rp2-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_20_0-samd-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_20_0-stm32-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_21_0-esp32-esp32_generic-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_21_0-esp32-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_21_0-rp2-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_21_0-samd-stubs/array.pyi class array(List): \n", - "publish/micropython-v1_21_0-stm32-stubs/array.pyi class array(List): \n", - "stubs/micropython-v1_17-docstubs/array.pyi class array: \n", - "stubs/micropython-v1_17-esp32-merged/array.py class array: \n", - "stubs/micropython-v1_17-esp32-merged/array.pyi class array: \n", - "stubs/micropython-v1_17-esp8266-merged/array.py class array: \n", - "stubs/micropython-v1_17-esp8266-merged/array.pyi class array: \n", - "stubs/micropython-v1_17-rp2-merged/array.py class array: \n", - "stubs/micropython-v1_17-rp2-merged/array.pyi class array: \n", - "stubs/micropython-v1_17-stm32-merged/array.py class array: \n", - "stubs/micropython-v1_17-stm32-merged/array.pyi class array: \n", - "stubs/micropython-v1_18-docstubs/array.pyi class array: \n", - "stubs/micropython-v1_18-esp32-merged/array.py class array: \n", - "stubs/micropython-v1_18-esp32-merged/array.pyi class array: \n", - "stubs/micropython-v1_18-esp8266-merged/array.py class array: \n", - "stubs/micropython-v1_18-esp8266-merged/array.pyi class array: \n", - "stubs/micropython-v1_18-rp2-merged/array.py class array: \n", - "stubs/micropython-v1_18-rp2-merged/array.pyi class array: \n", - "stubs/micropython-v1_18-stm32-merged/array.py class array: \n", - "stubs/micropython-v1_18-stm32-merged/array.pyi class array: \n", - "stubs/micropython-v1_19-docstubs/array.pyi class array: \n", - "stubs/micropython-v1_19_1-docstubs/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-esp32-GENERIC-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-esp32-GENERIC-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-esp32-GENERIC_S3-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-esp32-GENERIC_S3-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-esp32-GENERIC_SPIRAM-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-esp32-GENERIC_SPIRAM-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-esp32-UM_TINYPICO-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-esp32-UM_TINYPICO-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-esp8266-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-esp8266-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-rp2-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-rp2-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_19_1-stm32-merged/array.py class array(List): \n", - "stubs/micropython-v1_19_1-stm32-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-docstubs/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-esp32-GENERIC-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-esp32-GENERIC-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-esp32-GENERIC_OTA-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-esp32-GENERIC_OTA-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-esp32-GENERIC_S3-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-esp32-GENERIC_S3-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-rp2-PICO-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-rp2-PICO-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-rp2-PICO_W-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-rp2-PICO_W-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-rp2-PIMORONI_PICOLIPO_16MB-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-samd-ADAFRUIT_FEATHER_M4_EXPRESS-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-samd-ADAFRUIT_ITSYBITSY_M4_EXPRESS-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-samd-MINISAM_M4-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-samd-MINISAM_M4-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-samd-SEEED_WIO_TERMINAL-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-samd-SEEED_WIO_TERMINAL-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_20_0-stm32-PYBV11-merged/array.py class array(List): \n", - "stubs/micropython-v1_20_0-stm32-PYBV11-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_21_0-docstubs/array.pyi class array(List): \n", - "stubs/micropython-v1_21_0-esp32-ESP32_GENERIC-merged/array.py class array(List): \n", - "stubs/micropython-v1_21_0-esp32-ESP32_GENERIC-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_21_0-rp2-RPI_PICO-merged/array.py class array(List): \n", - "stubs/micropython-v1_21_0-rp2-RPI_PICO-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_21_0-rp2-RPI_PICO_W-merged/array.py class array(List): \n", - "stubs/micropython-v1_21_0-rp2-RPI_PICO_W-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged/array.py class array(List): \n", - "stubs/micropython-v1_21_0-samd-SEEED_WIO_TERMINAL-merged/array.pyi class array(List): \n", - "stubs/micropython-v1_21_0-stm32-PYBV11-merged/array.py class array(List): \n", - "stubs/micropython-v1_21_0-stm32-PYBV11-merged/array.pyi class array(List): \n" - ] - } - ], - "source": [ - "data = \"\"\n", - "for f in files:\n", - " with open(f, \"r\") as file:\n", - " data = file.read()\n", - " stat = \"?\"\n", - " if \"class array(List):\" in data:\n", - " stat = \"class array(List):\"\n", - " elif \"class array:\" in data:\n", - " stat = \"class array:\"\n", - " print(f\"{str(f.relative_to(stub_path.parent)):<80} {stat:<20}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.17" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/scripts/requirements.ps1 b/scripts/requirements.ps1 deleted file mode 100644 index ab2af19e3..000000000 --- a/scripts/requirements.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -# script used to prepare for testing different setups - -pip install -U micropython-stdlib-minimal --target ./typings --no-user -# pip install micropython-esp32-stubs --target ./typings --no-user - -$PyPi = $False - -if ($PyPi) { - pip install -U micropython-stm32-stubs --target .snippets/stm32/typings --no-user - pip install -U micropython-esp32-stubs --target .snippets/esp32/typings --no-user - pip install -U micropython-esp8266-stubs --target .snippets/esp8266/typings --no-user - pip install -U micropython-rp2-stubs --target .snippets/rp2/typings --no-user -} else { - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\micropython-v1_19_1-stm32-stubs --target .snippets/stm32/typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\micropython-v1_19_1-esp32-stubs --target .snippets/esp32/typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\micropython-v1_19_1-esp8266-stubs --target .snippets/esp8266/typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\micropython-v1_19_1-rp2-stubs --target .snippets/rp2/typings --no-user -} - -if (0){ - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\m\micropython-stdlib-minimal --target ./typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\m\micropython-stdlib-minimal --target .snippets/stm32/typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\m\micropython-stdlib-minimal --target .snippets/esp32/typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\m\micropython-stdlib-minimal --target .snippets/esp8266/typings --no-user - pip install -U C:\develop\MyPython\micropython-stubber\repos\micropython-stubs\publish\m\micropython-stdlib-minimal --target .snippets/rp2/typings --no-user - - -} - - - diff --git a/src/stubber/__init__.py b/src/stubber/__init__.py index aed888190..7bbc1a306 100644 --- a/src/stubber/__init__.py +++ b/src/stubber/__init__.py @@ -1,4 +1,4 @@ """get the version""" -__version__ = "1.16.2" +__version__ = "1.17.0" diff --git a/src/stubber/basicgit.py b/src/stubber/basicgit.py index f8802caaf..fa7644df5 100644 --- a/src/stubber/basicgit.py +++ b/src/stubber/basicgit.py @@ -13,11 +13,12 @@ from loguru import logger as log from packaging.version import parse -# Token with no permissions +# from stubber.utils.versions import SET_PREVIEW + +# Token with no permissions to avoid throttling +# https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#getting-a-higher-rate-limit PAT_NO_ACCESS = ( - "github_pat" - + "_11AAHPVFQ0IwtAmfc3cD5Z" - + "_xOVII22ErRzzZ7xwwxRcNotUu4krMMbjinQcsMxjnWkYFBIDRWFlZMaHSqq" + "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X" ) PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS GH_CLIENT = Github(auth=Auth.Token(PAT)) @@ -36,18 +37,18 @@ def _run_local_git( if isinstance(repo, str): repo = Path(repo) result = subprocess.run( - cmd, capture_output=capture_output, check=True, cwd=repo.absolute().as_posix() + cmd, capture_output=capture_output, check=True, cwd=repo.absolute().as_posix(), encoding="utf-8" ) else: - result = subprocess.run(cmd, capture_output=capture_output, check=True) + result = subprocess.run(cmd, capture_output=capture_output, check=True, encoding="utf-8") except (NotADirectoryError, FileNotFoundError) as e: # pragma: no cover return None except subprocess.CalledProcessError as e: # pragma: no cover # add some logging for github actions - log.error(f"{str(e)} : { e.stderr.decode('utf-8')}") + log.error(f"{str(e)} : { e.stderr}") return None if result.stderr and result.stderr != b"": - stderr = result.stderr.decode("utf-8") + stderr = result.stderr if "cloning into" in stderr.lower(): # log.info(stderr) expect_stderr = True @@ -59,8 +60,8 @@ def _run_local_git( if not expect_stderr: raise ChildProcessError(stderr) - if result.returncode < 0: - raise ChildProcessError(result.stderr.decode("utf-8")) + if result.returncode and result.returncode < 0: + raise ChildProcessError(result.stderr) return result @@ -69,7 +70,7 @@ def clone(remote_repo: str, path: Path, shallow: bool = False, tag: Optional[str cmd = ["git", "clone"] if shallow: cmd += ["--depth", "1"] - if tag in ("latest", "master"): + if tag in {"preview", "latest", "master"}: tag = None cmd += [remote_repo, "--branch", tag, str(path)] if tag else [remote_repo, str(path)] if result := _run_local_git(cmd, expect_stderr=True, capture_output=False): @@ -78,9 +79,7 @@ def clone(remote_repo: str, path: Path, shallow: bool = False, tag: Optional[str return False -def get_local_tag( - repo: Optional[Union[str, Path]] = None, abbreviate: bool = True -) -> Union[str, None]: +def get_local_tag(repo: Optional[Union[str, Path]] = None, abbreviate: bool = True) -> Union[str, None]: """ get the most recent git version tag of a local repo repo Path should be in the form of : repo = "./repo/micropython" @@ -100,23 +99,18 @@ def get_local_tag( ) if not result: return None - tag: str = result.stdout.decode("utf-8") + tag: str = result.stdout tag = tag.replace("\r", "").replace("\n", "") - if abbreviate and "-" in tag: - if result := _run_local_git( - ["git", "status", "--branch"], - repo=repo.as_posix(), - expect_stderr=True, - ): - lines = result.stdout.decode("utf-8").replace("\r", "").split("\n") - if lines[0].startswith("On branch") and lines[0].endswith("master"): - tag = "latest" + if not abbreviate or "-" not in tag: + return tag + if "-preview" in tag: + tag = tag.split("-preview")[0] + "-preview" return tag def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) -> List[str]: """ - get list of tag of a local repo + get list of all tags of a local repo """ if not repo: repo = Path(".") @@ -124,7 +118,7 @@ def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) -> result = _run_local_git(["git", "tag", "-l"], repo=repo.as_posix(), expect_stderr=True) if not result or result.returncode != 0: return [] - tags = result.stdout.decode("utf-8").replace("\r", "").split("\n") + tags = result.stdout.replace("\r", "").split("\n") tags = [tag for tag in tags if tag.startswith("v")] if minver: tags = [tag for tag in tags if parse(tag) >= parse(minver)] @@ -154,12 +148,12 @@ def checkout_tag(tag: str, repo: Optional[Union[str, Path]] = None) -> bool: """ checkout a specific git tag """ - cmd = ["git", "checkout", "tags/" + tag, "--detach", "--quiet", "--force"] + cmd = ["git", "checkout", tag, "--quiet", "--force"] result = _run_local_git(cmd, repo=repo, expect_stderr=True, capture_output=True) if not result: return False # actually a good result - msg = {result.stdout.decode("utf-8")} + msg = {result.stdout} if msg != {""}: log.warning(f"git message: {msg}") return True @@ -177,7 +171,7 @@ def sync_submodules(repo: Optional[Union[Path, str]] = None) -> bool: for cmd in cmds: if result := _run_local_git(cmd, repo=repo, expect_stderr=True): # actually a good result - log.debug(result.stderr.decode("utf-8")) + log.debug(result.stderr) else: return False return True @@ -192,11 +186,11 @@ def checkout_commit(commit_hash: str, repo: Optional[Union[Path, str]] = None) - if not result: return False # actually a good result - log.debug(result.stderr.decode("utf-8")) + log.debug(result.stderr) return True -def switch_tag(tag: str, repo: Optional[Union[Path, str]] = None) -> bool: +def switch_tag(tag: Union[str, Path], repo: Optional[Union[Path, str]] = None) -> bool: """ switch to the specified version tag of a local repo repo should be in the form of : path/.git @@ -209,7 +203,7 @@ def switch_tag(tag: str, repo: Optional[Union[Path, str]] = None) -> bool: if not result: return False # actually a good result - log.debug(result.stderr.decode("utf-8")) + log.debug(result.stderr) return True @@ -225,7 +219,7 @@ def switch_branch(branch: str, repo: Optional[Union[Path, str]] = None) -> bool: if not result: return False # actually a good result - log.debug(result.stderr.decode("utf-8")) + log.debug(result.stderr) return True diff --git a/src/stubber/board/board_info.csv b/src/stubber/board/board_info.csv index bfc5016bc..9e9cb6aaa 100644 --- a/src/stubber/board/board_info.csv +++ b/src/stubber/board/board_info.csv @@ -1,115 +1,101 @@ board,description -LaunchPad with CC3200,LAUNCHXL -WiPy with CC3200,WIPY -ESP32-S2-WROVER with ESP32-S2,ESP32_S2_WROVER -ESP32 module with ESP32,GENERIC -ESP32C3 module with ESP32C3,GENERIC_C3 -ESP32C3 module with ESP32C3,GENERIC_C3_USB -Generic ESP32-D2WD module with ESP32-D2WD,GENERIC_D2WD 4MB/OTA module with ESP32,GENERIC_OTA -ESP32S2 module with ESP32S2,GENERIC_S2 -ESP32S3 module with ESP32S3,GENERIC_S3 -ESP32S3 module (spiram) with ESP32S3,GENERIC_S3_SPIRAM -ESP32S3 module (spiram octal) with ESP32S3,GENERIC_S3_SPIRAM_OCT +Actinius Icarus with NRF9160,ACTINIUS_ICARUS +Adafruit Feather RP2040,ADAFRUIT_FEATHER_RP2040 +Adafruit Feather STM32F405 with STM32F405RG,ADAFRUIT_F405_EXPRESS +Adafruit ItsyBitsy RP2040,ADAFRUIT_ITSYBITSY_RP2040 +Adafruit Metro M7 with MIMXRT1011DAE5A,ADAFRUIT_METRO_M7 +Adafruit QT Py RP2040,ADAFRUIT_QTPY_RP2040 +Arduino GIGA R1 WiFi with STM32H747,ARDUINO_GIGA +Arduino Nano 33 BLE Sense with NRF52840,ARDUINO_NANO_33_BLE_SENSE +Arduino Nano ESP32 with ESP32S3,ARDUINO_NANO_ESP32 +Arduino Nano RP2040 Connect,ARDUINO_NANO_RP2040_CONNECT +Arduino Nicla Vision with STM32H747,ARDUINO_NICLA_VISION +Arduino Portenta C33 with RA6M5,ARDUINO_PORTENTA_C33 +Arduino Portenta H7 with STM32H747,ARDUINO_PORTENTA_H7 +Arduino Primo with NRF52832,ARDUINO_PRIMO +B-L072Z-LRWAN1 with STM32L072CZ,B_L072Z_LRWAN1 +B-L475E-IOT01A with STM32L475,B_L475E_IOT01A +Bluefruit nRF52 Feather with NRF52832,FEATHER52 +BLUEIO-TAG-EVIM with NRF52832,BLUEIO_TAG_EVIM +Cerb40 with STM32F405RG,CERB40 +CustomPCB with STM32F439,STM32F439 +DVK-BL652 with NRF52832,DVK_BL652 +EK-RA4M1 with RA4M1,EK_RA4M1 +EK-RA4W1 with RA4W1,EK_RA4W1 +EK-RA6M1 with RA6M1,EK_RA6M1 +EK-RA6M2 with RA6M2,EK_RA6M2 +ESP module (1M) with ESP8266,ESP8266_GENERIC +ESP module (512K) with ESP8266,ESP8266_GENERIC +ESP module with ESP8266,ESP8266_GENERIC ESP32 module (spiram) with ESP32,GENERIC_SPIRAM +ESP32 module with ESP32,GENERIC ESP32 Unicore module with ESP32-UNICORE,GENERIC_UNICORE -LILYGO TTGO LoRa32 with ESP32,LILYGO_TTGO_LORA32 -LOLIN_C3_MINI with ESP32-C3FH4,LOLIN_C3_MINI -LOLIN_S2_MINI with ESP32-S2FN4R2,LOLIN_S2_MINI -LOLIN_S2_PICO with ESP32-S2FN4R2,LOLIN_S2_PICO -M5Stack ATOM with ESP32-PICO-D4,M5STACK_ATOM -Olimex ESP32 ETH with ESP32,OLIMEX_ESP32_POE -Silicognition wESP32 with ESP32,SIL_WESP32 -FeatherS2 with ESP32-S2,UM_FEATHERS2 +ESP32-D2WD,ESP32_GENERIC +ESP32-S2-WROVER with ESP32-S2,ESP32_S2_WROVER +ESP32-UNICORE,ESP32_GENERIC +ESP32C3 module with ESP32C3,ESP32_GENERIC_C3 +ESP32S2 module with ESP32S2,GENERIC_S2 +ESP32S3 module (spiram octal) with ESP32S3,GENERIC_S3_SPIRAM_OCT +ESP32S3 module (spiram) with ESP32S3,GENERIC_S3_SPIRAM +ESP32S3 module with ESP32S3,GENERIC_S3 +Espruino Pico with STM32F401CD,ESPRUINO_PICO +EVK_NINA_B1 with NRF52832,EVK_NINA_B1 +EVK_NINA_B3 with NRF52840,EVK_NINA_B3 +F411DISC with STM32F411,STM32F411DISC +F429I-DISCO with STM32F429,STM32F429DISC +F4DISC with STM32F407,STM32F4DISC +F769DISC with STM32F769,STM32F769DISC +F7DISC with STM32F746,STM32F7DISC +Feather M0 Express with SAMD21G18A,ADAFRUIT_FEATHER_M0_EXPRESS +Feather M4 Express with SAMD51J19A,ADAFRUIT_FEATHER_M4_EXPRESS FeatherS2 Neo with ESP32-S2FN4R2,UM_FEATHERS2NEO +FeatherS2 with ESP32-S2,UM_FEATHERS2 FeatherS3 with ESP32-S3,UM_FEATHERS3 -ProS3 with ESP32-S3,UM_PROS3 -TinyPICO with ESP32-PICO-D4,UM_TINYPICO -TinyS2 with ESP32-S2FN4R2,UM_TINYS2 -TinyS3 with ESP32-S3-FN8,UM_TINYS3 -ESP module with ESP8266,GENERIC -ESP module (1M) with ESP8266,GENERIC_1M -ESP module (512K) with ESP8266,GENERIC_512K +GARATRONIC_PYBSTICK26_RP2040,GARATRONIC_PYBSTICK26_RP2040 +Generic ESP32 module with ESP32,ESP32_GENERIC +Generic ESP32 module with OTA,ESP32_GENERIC +Generic ESP32 module with SPIRAM,ESP32_GENERIC +Generic ESP32-D2WD module with ESP32-D2WD,GENERIC_D2WD +Generic ESP32S2 module with ESP32S2,ESP32_GENERIC_S2 +Generic ESP32S3 module with ESP32S3,ESP32_GENERIC_S3 +Generic ESP32S3 module with Octal-SPIRAM,ESP32_GENERIC_S3 +GIGA with STM32H747,ARDUINO_GIGA +HydraBus1.0 with STM32F4,HYDRABUS i.MX RT1010 EVK with MIMXRT1011DAE5A,MIMXRT1010_EVK i.MX RT1015 EVK with MIMXRT1015DAF5A,MIMXRT1015_EVK i.MX RT1020 EVK with MIMXRT1021DAG5A,MIMXRT1020_EVK +i.MX RT1050 EVK with MIMXRT1052DVL6B,MIMXRT1050_EVK +i.MX RT1050 EVKB with MIMXRT1052DVL6B,MIMXRT1050_EVKB i.MX RT1050 EVKB-A1 with MIMXRT1052DVL6B,MIMXRT1050_EVK i.MX RT1060 EVK with MIMXRT1062DVJ6A,MIMXRT1060_EVK i.MX RT1064 EVK with MIMXRT1064DVL6A,MIMXRT1064_EVK i.MX RT1170 EVK with MIMXRT1176DVMAA,MIMXRT1170_EVK -RT1010-Py-DevKIT with MIMXRT1011DAE5A,OLIMEX_RT1010 -Seeed ARCH MIX with MIMXRT1052DVL5B,SEEED_ARCH_MIX -Teensy 4.0 with MIMXRT1062DVJ6A,TEENSY40 -Teensy 4.1 with MIMXRT1062DVJ6A,TEENSY41 -Actinius Icarus with NRF9160,actinius_icarus -Arduino Nano 33 BLE Sense with NRF52840,arduino_nano_33_ble_sense -Arduino Primo with NRF52832,arduino_primo -BLUEIO-TAG-EVIM with NRF52832,blueio_tag_evim -DVK-BL652 with NRF52832,dvk_bl652 -EVK_NINA_B1 with NRF52832,evk_nina_b1 -EVK_NINA_B3 with NRF52840,evk_nina_b3 -Bluefruit nRF52 Feather with NRF52832,feather52 -IBK-BLYST-NANO with NRF52832,ibk_blyst_nano -IDK-BLYST-NANO with NRF52832,idk_blyst_nano -micro:bit with NRF51822,microbit -MDK-USB-DONGLE with NRF52840,nrf52840-mdk-usb-dongle -XENON with NRF52840,particle_xenon -PCA10000 with NRF51822,pca10000 -PCA10001 with NRF51822,pca10001 -PCA10028 with NRF51822,pca10028 -PCA10031 with NRF51822,pca10031 -PCA10040 with NRF52832,pca10040 -PCA10056 with NRF52840,pca10056 -PCA10059 with NRF52840,pca10059 -PCA10090 with NRF9160,pca10090 -XIAO nRF52840 Sense with NRF52840,seeed_xiao_nrf52 -WT51822-S4AT with NRF51822,wt51822_s4at -RA4M1_CLICKER with RA4M1,RA4M1_CLICKER -RA4M1_EK with RA4M1,RA4M1_EK -RA4W1_EK with RA4W1,RA4W1_EK -RA6M1_EK with RA6M1,RA6M1_EK -RA6M2_EK with RA6M2,RA6M2_EK -Adafruit Feather RP2040,ADAFRUIT_FEATHER_RP2040 -Adafruit ItsyBitsy RP2040,ADAFRUIT_ITSYBITSY_RP2040 -Adafruit QT Py RP2040,ADAFRUIT_QTPY_RP2040 -Arduino Nano RP2040 Connect,ARDUINO_NANO_RP2040_CONNECT -GARATRONIC_PYBSTICK26_RP2040,GARATRONIC_PYBSTICK26_RP2040 -nullbits Bit-C PRO,NULLBITS_BIT_C_PRO -Raspberry Pi Pico,PICO -Raspberry Pi Pico W,PICO_W -Pimoroni Pico LiPo 16MB,PIMORONI_PICOLIPO_16MB -Pimoroni Pico LiPo 4MB,PIMORONI_PICOLIPO_4MB -Pimoroni Tiny 2040,PIMORONI_TINY2040 -SparkFun Pro Micro RP2040,SPARKFUN_PROMICRO -SparkFun Thing Plus RP2040,SPARKFUN_THINGPLUS -W5100S-EVB-Pico,W5100S_EVB_PICO -W5500-EVB-Pico,W5500_EVB_PICO -WeAct Studio RP2040,WEACTSTUDIO -Feather M0 Express with SAMD21G18A,ADAFRUIT_FEATHER_M0_EXPRESS -Feather M4 Express with SAMD51J19A,ADAFRUIT_FEATHER_M4_EXPRESS +IBK-BLYST-NANO with NRF52832,IBK_BLYST_NANO +IDK-BLYST-NANO with NRF52832,IDK_BLYST_NANO ItsyBitsy M0 Express with SAMD21G18A,ADAFRUIT_ITSYBITSY_M0_EXPRESS ItsyBitsy M4 Express with SAMD51G19A,ADAFRUIT_ITSYBITSY_M4_EXPRESS -Trinket M0 with SAMD21E18A,ADAFRUIT_TRINKET_M0 -Mini SAM M4 with SAMD51G19A,MINISAM_M4 -SAMD21-XPLAINED-PRO with SAMD21J18A,SAMD21_XPLAINED_PRO -Wio Terminal D51R with SAMD51P19A,SEEED_WIO_TERMINAL -Seeed Xiao with SAMD21G18A,SEEED_XIAO_SAMD21 -Sparkfun SAMD51 Thing Plus with SAMD51J20A,SPARKFUN_SAMD51_THING_PLUS -Adafruit Feather STM32F405 with STM32F405RG,ADAFRUIT_F405_EXPRESS -PORTENTA with STM32H747,ARDUINO_PORTENTA_H7 -B-L072Z-LRWAN1 with STM32L072CZ,B_L072Z_LRWAN1 -B-L475E-IOT01A with STM32L475,B_L475E_IOT01A -Cerb40 with STM32F405RG,CERB40 -Espruino Pico with STM32F401CD,ESPRUINO_PICO -NADHAT_PYBF405 with STM32F405RG,GARATRONIC_NADHAT_F405 -PYBSTICK26_STD with STM32F411RE,GARATRONIC_PYBSTICK26_F411 -HydraBus1.0 with STM32F4,HYDRABUS +L476-DISCO with STM32L476,STM32L476DISC +L496G-DISCO with STM32L496,STM32L496GDISC +LaunchPad with CC3200,LAUNCHXL LEGO Technic Hub No.6 with STM32F413,LEGO_HUB_NO6 LEGO Technic Hub No.7 with STM32F413,LEGO_HUB_NO7 +LILYGO TTGO LoRa32 with ESP32,LILYGO_TTGO_LORA32 LIMIFROG with STM32L476,LIMIFROG -MIKROE_CLICKER2_STM32 with STM32F407,MIKROE_CLICKER2_STM32 +LOLIN_C3_MINI with ESP32-C3FH4,LOLIN_C3_MINI +LOLIN_S2_MINI with ESP32-S2FN4R2,LOLIN_S2_MINI +LOLIN_S2_PICO with ESP32-S2FN4R2,LOLIN_S2_PICO +M5Stack ATOM with ESP32-PICO-D4,M5STACK_ATOM +MDK-USB-DONGLE with NRF52840,NRF52840_MDK_USB_DONGLE +Metro M4 Express Airlift with SAMD51J19A,ADAFRUIT_METRO_M4_EXPRESS +micro:bit with NRF51822,MICROBIT MikroE Quail with STM32F427VI,MIKROE_QUAIL +MIKROE_CLICKER2_STM32 with STM32F407,MIKROE_CLICKER2_STM32 +Mini SAM M4 with SAMD51G19A,MINISAM_M4 +NADHAT_PYBF405 with STM32F405RG,GARATRONIC_NADHAT_F405 +NanoS3 with ESP32-S3-FN8,UM_NANOS3 NetduinoPlus2 with STM32F405RG,NETDUINO_PLUS_2 +NICLAVISION with STM32H747,ARDUINO_NICLA_VISION NUCLEO-F091RC with STM32F091RCT6,NUCLEO_F091RC NUCLEO-F401RE with STM32F401xE,NUCLEO_F401RE NUCLEO-F411RE with STM32F411xE,NUCLEO_F411RE @@ -123,37 +109,85 @@ NUCLEO-F746ZG with STM32F746,NUCLEO_F746ZG NUCLEO-F756ZG with STM32F756,NUCLEO_F756ZG NUCLEO-F767ZI with STM32F767,NUCLEO_F767ZI NUCLEO-G0B1RE with STM32G0B1xE,NUCLEO_G0B1RE -NUCLEO_G474RE with STM32G474,NUCLEO_G474RE -NUCLEO_H743ZI with STM32H743,NUCLEO_H743ZI -NUCLEO_H743ZI2,NUCLEO_H743ZI2 NUCLEO-L073RZ with STM32L073RZT6,NUCLEO_L073RZ NUCLEO-L152RE with STM32L152xE,NUCLEO_L152RE NUCLEO-L432KC with STM32L432KC,NUCLEO_L432KC NUCLEO-L452RE with STM32L452RE,NUCLEO_L452RE NUCLEO-L476RG with STM32L476RG,NUCLEO_L476RG +NUCLEO-L4A6ZG with STM32L4A6ZG,NUCLEO_L4A6ZG NUCLEO-WB55 with STM32WB55RGV6,NUCLEO_WB55 NUCLEO-WL55 with STM32WL55JCI7,NUCLEO_WL55 +NUCLEO_G474RE with STM32G474,NUCLEO_G474RE +NUCLEO_H563ZI with STM32H563ZI,NUCLEO_H563ZI +NUCLEO_H723ZG with STM32H723ZGT6,NUCLEO_H723ZG +NUCLEO_H743ZI with STM32H743,NUCLEO_H743ZI +NUCLEO_H743ZI2,NUCLEO_H743ZI2 +nullbits Bit-C PRO,NULLBITS_BIT_C_PRO +Olimex ESP32 ETH with ESP32,OLIMEX_ESP32_POE OLIMEX STM32-E407 with STM32F407,OLIMEX_E407 OLIMEX STM32-H407 with STM32F407,OLIMEX_H407 +PCA10000 with NRF51822,PCA10000 +PCA10001 with NRF51822,PCA10001 +PCA10028 with NRF51822,PCA10028 +PCA10031 with NRF51822,PCA10031 +PCA10040 with NRF52832,PCA10040 +PCA10056 with NRF52840,PCA10056 +PCA10059 with NRF52840,PCA10059 +PCA10090 with NRF9160,PCA10090 +Pimoroni Pico LiPo 16MB,PIMORONI_PICOLIPO_16MB +Pimoroni Pico LiPo 4MB,PIMORONI_PICOLIPO_4MB +Pimoroni Tiny 2040,PIMORONI_TINY2040 +Pololu 3pi+ 2040 Robot,POLOLU_3PI_2040_ROBOT +Pololu Zumo 2040 Robot,POLOLU_ZUMO_2040_ROBOT +PORTENTA C33 with RA6M5,ARDUINO_PORTENTA_C33 +PORTENTA with STM32H747,ARDUINO_PORTENTA_H7 +ProS3 with ESP32-S3,UM_PROS3 PYBD-SF2W with STM32F722IEK,PYBD_SF2 PYBD-SF3W with STM32F733IEK,PYBD_SF3 PYBD-SF6W with STM32F767IIK,PYBD_SF6 PYBLITEv1.0 with STM32F411RE,PYBLITEV10 +PYBSTICK26_STD with STM32F411RE,GARATRONIC_PYBSTICK26_F411 PYBv1.0 with STM32F405RG,PYBV10 PYBv1.1 with STM32F405RG,PYBV11 PYBv3 with STM32F405RG,PYBV3 PYBv4 with STM32F405RG,PYBV4 +RA4M1 CLICKER with RA4M1,RA4M1_CLICKER +RA4M1_CLICKER with RA4M1,RA4M1_CLICKER +RA4M1_EK with RA4M1,RA4M1_EK +RA4W1_EK with RA4W1,RA4W1_EK +RA6M1_EK with RA6M1,RA6M1_EK +RA6M2_EK with RA6M2,RA6M2_EK +Raspberry Pi Pico,RPI_PICO +Raspberry Pi Pico W,RPI_PICO_W +RT1010-Py-DevKIT with MIMXRT1011DAE5A,OLIMEX_RT1010 +SAMD21-XPLAINED-PRO with SAMD21J18A,SAMD21_XPLAINED_PRO +Seeed ARCH MIX with MIMXRT1052DVL5B,SEEED_ARCH_MIX +Seeed Xiao with SAMD21G18A,SEEED_XIAO_SAMD21 +Silicognition RP2040-Shim,SIL_RP2040_SHIM +Silicognition wESP32 with ESP32,SIL_WESP32 +SparkFun Pro Micro RP2040,SPARKFUN_PROMICRO +Sparkfun SAMD51 Thing Plus with SAMD51J20A,SPARKFUN_SAMD51_THING_PLUS SparkFun STM32 MicroMod Processor with STM32F405RG,SPARKFUN_MICROMOD_STM32 -F411DISC with STM32F411,STM32F411DISC -F429I-DISCO with STM32F429,STM32F429DISC -CustomPCB with STM32F439,STM32F439 -F4DISC with STM32F407,STM32F4DISC -F769DISC with STM32F769,STM32F769DISC -F7DISC with STM32F746,STM32F7DISC +SparkFun Thing Plus RP2040,SPARKFUN_THINGPLUS +STM32H573I-DK with STM32H573IIK3Q,STM32H573I_DK STM32H7B3I-DK with STM32H7B3LIH6Q,STM32H7B3I_DK -L476-DISCO with STM32L476,STM32L476DISC -L496G-DISCO with STM32L496,STM32L496GDISC +Teensy 4.0 with MIMXRT1062DVJ6A,TEENSY40 +Teensy 4.1 with MIMXRT1062DVJ6A,TEENSY41 +TinyPICO with ESP32-PICO-D4,UM_TINYPICO +TinyS2 with ESP32-S2FN4R2,UM_TINYS2 +TinyS3 with ESP32-S3-FN8,UM_TINYS3 +TinyWATCH S3 with ESP32-S3-PICO-1-N8R2,UM_TINYWATCHS3 +Trinket M0 with SAMD21E18A,ADAFRUIT_TRINKET_M0 USBDongle-WB55 with STM32WB55CGU6,USBDONGLE_WB55 VCC-GND STM32F407VE with STM32F407VE,VCC_GND_F407VE VCC-GND STM32F407ZG with STM32F407ZG,VCC_GND_F407ZG VCC-GND STM32H743VI with STM32H743VI,VCC_GND_H743VI +VK-RA6M5 with RA6M5,VK_RA6M5 +W5100S-EVB-Pico,W5100S_EVB_PICO +W5500-EVB-Pico,W5500_EVB_PICO +WeAct Studio RP2040,WEACTSTUDIO +Wio Terminal D51R with SAMD51P19A,SEEED_WIO_TERMINAL +WiPy with CC3200,WIPY +WT51822-S4AT with NRF51822,WT51822_S4AT +XENON with NRF52840,PARTICLE_XENON +XIAO nRF52840 Sense with NRF52840,SEEED_XIAO_NRF52 diff --git a/src/stubber/board/createstubs.py b/src/stubber/board/createstubs.py index eeaea1fb7..2ab22f8ed 100644 --- a/src/stubber/board/createstubs.py +++ b/src/stubber/board/createstubs.py @@ -4,11 +4,14 @@ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -20,11 +23,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.2" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -32,24 +73,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -59,11 +97,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -82,20 +120,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -110,11 +151,7 @@ def get_obj_attributes(self, item_instance: object): order = 4 _result.append((name, repr(val), repr(type(val)), val, order)) except AttributeError as e: - _errors.append( - "Couldn't get attribute '{}' from object '{}', Err: {}".format( - name, item_instance, e - ) - ) + _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e)) except MemoryError as e: print("MemoryError: {}".format(e)) sleep(1) @@ -132,21 +169,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -161,10 +200,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -178,12 +217,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info( - "Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1) - ) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -195,40 +232,35 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: module_name, self._fwid, info_, __version__ ) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write( + "from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n" + ) self.write_object_stub(fp, new_module, module_name, "") - self._report.append( - '{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/")) - ) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True - def write_object_stub( - self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0 - ): + def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0): "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -236,11 +268,16 @@ def write_object_stub( # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -259,11 +296,11 @@ def write_object_stub( if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -277,11 +314,7 @@ def write_object_stub( s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug( - "# def {1} function/method/closure, type = '{0}'".format( - item_type_txt, item_name - ) - ) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -292,16 +325,14 @@ def write_object_stub( first = "self, " # class method - add function decoration if "bound_method" in item_type_txt or "bound_method" in item_repr: - s = "{}@classmethod\n".format( - indent - ) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret) - else: - s = "{}def {}({}*args, **kwargs) -> {}:\n".format( - indent, item_name, first, ret + s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format( + indent, item_name, ret ) + else: + s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -311,38 +342,42 @@ def write_object_stub( t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format( - indent, item_name, t, item_type_txt, item_repr - ) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -358,7 +393,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -376,45 +411,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info( - "Created stubs for {} modules on board {}\nPath: {}".format( - len(self._report), self._fwid, self.path - ) - ) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -440,23 +483,32 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", "port": sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", @@ -470,60 +522,46 @@ def _info(): # type:() -> dict[str, str] elif info["port"] == "linux": info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = ( - sys.implementation._machine - if "_machine" in dir(sys.implementation) - else os.uname().machine + _machine = ( + sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore ) - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -545,14 +583,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") - and info["version"] - >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -576,25 +614,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -647,10 +691,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -915,16 +955,9 @@ def main(): gc.collect() stubber.create_all_stubs() - stubber.report() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/src/stubber/board/createstubs_db.py b/src/stubber/board/createstubs_db.py index 8777b0c73..38f14d899 100644 --- a/src/stubber/board/createstubs_db.py +++ b/src/stubber/board/createstubs_db.py @@ -18,16 +18,19 @@ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device -This variant was generated from createstubs.py by micropython-stubber v1.16.2 +This variant was generated from createstubs.py by micropython-stubber v1.17.0 """ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -39,11 +42,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.2" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -51,24 +92,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -78,11 +116,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -101,20 +139,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -147,21 +188,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -176,10 +219,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -193,10 +236,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -206,21 +249,18 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") - self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True @@ -228,14 +268,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -243,11 +283,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -266,11 +311,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -284,7 +329,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -300,7 +345,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -310,36 +355,42 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -355,7 +406,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -373,41 +424,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -433,23 +496,32 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", "port": sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", @@ -463,56 +535,44 @@ def _info(): # type:() -> dict[str, str] elif info["port"] == "linux": info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -534,13 +594,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -564,25 +625,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -635,10 +702,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -652,92 +715,99 @@ def is_micropython() -> bool: return True -def main(): - import machine # type: ignore +SKIP_FILE = "modulelist.done" + +def get_modules(skip=0): + # new + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + try: + with open(fname) as f: + i = 0 + while True: + line = f.readline().strip() + if not line: + break + if len(line) > 0 and line[0] == "#": + continue + i += 1 + if i < skip: + continue + yield line + break + except OSError: + pass + + +def write_skip(done): + # write count of modules already processed to file + with open(SKIP_FILE, "w") as f: + f.write(str(done) + "\n") + + +def read_skip(): + # read count of modules already processed from file + done = 0 try: - f = open("modulelist.done", "r+b") - was_running = True - print("Opened existing db") + with open(SKIP_FILE) as f: + done = int(f.readline().strip()) except OSError: - f = open("modulelist.done", "w+b") - print("created new db") - was_running = False + pass + return done + + +def main(): + import machine # type: ignore + + was_running = file_exists(SKIP_FILE) + if was_running: + log.info("Continue from last run") + else: + log.info("Starting new run") + # try: + # f = open("modulelist.done", "r+b") + # was_running = True + # print("Continue from last run") + # except OSError: + # f = open("modulelist.done", "w+b") + # was_running = False stubber = Stubber(path=read_path()) # f_name = "{}/{}".format(stubber.path, "modules.json") + skip = 0 if not was_running: # Only clean folder if this is a first run stubber.clean() - # get list of modules to process - get_modulelist(stubber) - # remove the ones that are already done - modules_done = {} # type: dict[str, str] - try: - with open("modulelist.done") as f: - # not optimal , but works on mpremote and esp8266 - for line in f.read().split("\n"): - line = line.strip() - gc.collect() - if len(line) > 0: - key, value = line.split("=", 1) - modules_done[key] = value - except (OSError, SyntaxError): - pass - gc.collect() - # see if we can continue from where we left off - modules = [m for m in stubber.modules if m not in modules_done.keys()] - gc.collect() - for modulename in modules: + stubber.report_start("modules.json") + else: + skip = read_skip() + stubber._json_name = "{}/{}".format(stubber.path, "modules.json") + + for modulename in get_modules(skip): # ------------------------------------ # do epic shit # but sometimes things fail / run out of memory and reboot - ok = False try: - ok = stubber.create_one_stub(modulename) + stubber.create_one_stub(modulename) except MemoryError: # RESET AND HOPE THAT IN THE NEXT CYCLE WE PROGRESS FURTHER machine.reset() # ------------------------------------- gc.collect() - modules_done[modulename] = str(stubber._report[-1] if ok else "failed") - with open("modulelist.done", "a") as f: - f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + # modules_done[modulename] = str(stubber._report[-1] if ok else "failed") + # with open("modulelist.done", "a") as f: + # f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + skip += 1 + write_skip(skip) - # Finished processing - load all the results , and remove the failed ones - if modules_done: - # stubber.write_json_end(mod_fp) - stubber._report = [v for _, v in modules_done.items() if v != "failed"] - stubber.report() - - -def get_modulelist(stubber): - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - with open(p + "/modulelist.txt") as f: - print("DEBUG: list of modules: " + p + "/modulelist.txt") - for line in f.read().split("\n"): - line = line.strip() - if len(line) > 0 and line[0] != "#": - stubber.modules.append(line) - gc.collect() - break - except OSError: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - _log.warn("Could not find modulelist.txt, using default modules") - gc.collect() + print("All modules have been processed, Finalizing report") + stubber.report_end() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/src/stubber/board/createstubs_db_min.py b/src/stubber/board/createstubs_db_min.py index 34ba0bee4..f3bc2d297 100644 --- a/src/stubber/board/createstubs_db_min.py +++ b/src/stubber/board/createstubs_db_min.py @@ -1,304 +1,325 @@ -v='stubber' -u='{}/{}' -t='method' -s='function' -r='bool' -q='str' -p='float' -o='int' -n=NameError -m=sorted -l=MemoryError -k=NotImplementedError -b=',\n' -a='dict' -Z='list' -Y='tuple' -X='micropython' -W=str -V=repr -T='_' -S=KeyError -R=IndexError -Q=dir -P=ImportError -O='family' -N=print -M=True -L=len -K='board' +A2='No report file' +A1='Failed to create the report.' +A0='method' +z='function' +y='bool' +x='str' +w='float' +v='int' +u='micropython' +t='stubber' +s=TypeError +r=Exception +q=KeyError +p=sorted +o=MemoryError +n=NotImplementedError +j=',\n' +i='modules.json' +h='{}/{}' +g='w' +f='dict' +e='list' +d='tuple' +c=str +b=repr +W='-preview' +V='-' +U='board' +T=IndexError +S=print +R=True +Q='family' +P=len +O=ImportError +N=dir +M=open +K='port' J='.' -I=open -H=AttributeError +I=AttributeError +H=False G='/' -F=False E=None -D='version' -A=OSError -C='' -import gc as B,os,sys -from ujson import dumps as c -try:from machine import reset -except P:pass -try:from collections import OrderedDict as d -except P:from ucollections import OrderedDict as d -__version__='v1.16.2' -w=2 -x=2 -e=[J,'/lib','/sd/lib','/flash/lib','lib'] +D=OSError +C='version' +B='' +import gc as F,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except O:pass +try:from collections import OrderedDict as k +except O:from ucollections import OrderedDict as k +__version__='v1.17.0' +A3=2 +A4=2 +A5=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(t) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(C,path=E,firmware_id=E): - D=firmware_id + def __init__(B,path=E,firmware_id=E): + C=firmware_id try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise k('MicroPython 1.13.0 cannot be stubbed') - except H:pass - C._report=[];C.info=_info();B.collect() - if D:C._fwid=D.lower() - elif C.info[O]==X:C._fwid='{family}-{ver}-{port}-{board}'.format(**C.info) - else:C._fwid='{family}-{ver}-{port}'.format(**C.info) - C._start_free=B.mem_free() + if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise n('MicroPython 1.13.0 cannot be stubbed') + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));F.collect() + if C:B._fwid=C.lower() + elif B.info[Q]==u:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=F.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - C.path='{}/stubs/{}'.format(path,C.flat_fwid).replace('//',G) - try:f(path+G) - except A:N('error creating stub folder {}'.format(path)) - C.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];C.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];C.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:X(path+G) + except D:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in Q(I): - if A.startswith(T)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in N(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=V(type(E)).split("'")[1] - except R:F=C - if F in{o,p,q,r,Y,Z,a}:G=1 - elif F in{s,t}:G=2 - elif F in'class':G=3 + D=getattr(H,A) + try:E=b(type(D)).split("'")[1] + except T:E=B + if E in{v,w,x,y,d,e,f}:G=1 + elif E in{z,A0}:G=2 + elif E in'class':G=3 else:G=4 - D.append((A,V(E),V(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except l as K:sleep(1);reset() - D=m([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);B.collect();return D,J - def add_modules(A,modules):A.modules=m(set(A.modules)|set(modules)) - def create_all_stubs(A): - B.collect() - for C in A.modules:A.create_one_stub(C) + C.append((A,b(D),b(type(D)),D,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except o as J:S('MemoryError: {}'.format(J));sleep(1);reset() + C=p([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K + def add_modules(A,modules):A.modules=p(set(A.modules)|set(modules)) + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') def create_one_stub(C,module_name): - D=module_name - if D in C.problematic:return F - if D in C.excluded:return F - H='{}/{}.py'.format(C.path,D.replace(J,G));B.collect();E=F - try:E=C.create_module_stub(D,H) - except A:return F - B.collect();return E + B=module_name + if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H + if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H + I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();E=H + try:E=C.create_module_stub(B,I) + except D:return H + F.collect();return E def create_module_stub(K,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(J,T)+'.py';H=K.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,J) - L=E - try:L=__import__(D,E,E,'*');U=B.mem_free() - except P:return F - f(H) - with I(H,'w')as N:Q=W(K.info).replace('OrderedDict(',C).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,K._fwid,Q,__version__);N.write(R);N.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');K.write_object_stub(N,L,D,C) - K._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del L - except(A,S):pass - try:del sys.modules[D] - except S:pass - B.collect();return M + I=file_name;C=module_name + if I is E:L=C.replace(J,'_')+'.pyi';I=K.path+G+L + else:L=I.split(G)[-1] + if G in C:C=C.replace(G,J) + N=E + try:N=__import__(C,E,E,'*');Q=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,Q)) + except O:return H + X(I) + with M(I,g)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) + K.report_add(C,I) + if C not in{'os','sys','logging','gc'}: + try:del N + except(D,q):A.warning('could not del new_module') + F.collect();return R def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';Q=in_class;P=object_expr;O='Exception';H=fp;E=indent;B.collect() - if P in K.problematic:return - R,M=K.get_obj_attributes(P) - if M:N(M) - for(F,J,G,T,f)in R: - if F in['classmethod','staticmethod','BaseException',O]:continue - if F[0].isdigit():continue - if G==""and L(E)<=x*4: - U=C;V=F.endswith(O)or F.endswith('Error')or F in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=O - D='\n{}class {}({}):\n'.format(E,F,U) - if V:D+=E+' ...\n';H.write(D);return - H.write(D);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,F),E+' ',Q+1);D=E+' def __init__(self, *argv, **kwargs) -> None:\n';D+=E+' ...\n\n';H.write(D) - elif any(A in G for A in[t,s,'closure']): - W=b;X=C - if Q>0:X='self, ' - if c in G or c in J:D='{}@classmethod\n'.format(E)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(E,F,W) - else:D='{}def {}({}*args, **kwargs) -> {}:\n'.format(E,F,X,W) - D+=E+' ...\n\n';H.write(D) - elif G=="":0 - elif G.startswith(""and P(D)<=A4*4: + Q=B;R=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] + if R:Q=M + C='\n{}class {}({}):\n'.format(D,E,Q) + if R:C+=D+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C) + elif any(A in H for A in[A0,z,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S) + C+=D+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=C - if A[D]==C and sys.platform not in(k,j): - try:o=os.uname();A[D]=o.release - except(R,H,TypeError):pass - for(p,q,r)in[(l,l,'const'),(m,m,'FAT'),(n,'pybricks.hubs','EV3Brick')]: - try:s=__import__(q,E,E,r);A[O]=p;del s;break - except(P,S):pass - if A[O]==n:A['release']='2.0.0' - if A[O]==X: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if I in A and A[I]: - V=int(A[I]);a=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][V>>10] - if a:A[f]=a - A[I]='v{}.{}'.format(V&255,V>>8&3) - A[b]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def g(info,board_descr,filename): - with I(filename,'r')as B: - while 1: - A=B.readline() - if not A:break - C,D=A.split(',')[0].strip(),A.split(',')[1].strip() - if C==board_descr:info[K]=D;return M - return F -def get_root(): - try:B=os.getcwd() - except(A,H):B=J - C=B - for C in[B,'/sd','/flash',G,J]: - try:D=os.stat(C);break - except A:continue + if'uname'in N(os): + A[D]=Y(os.uname()[3]) + if not A[D]:A[D]=Y(os.uname()[2]) + elif C in N(sys):A[D]=Y(sys.version) + except(I,T,s):pass + if A[C]==B and sys.platform not in(S,R): + try:a=os.uname();A[C]=a.release + except(T,I,s):pass + for(b,c,d)in[(V,V,'const'),(X,X,'FAT'),(Z,'pybricks.hubs','EV3Brick')]: + try:e=__import__(c,E,E,d);A[Q]=b;del e;break + except(O,q):pass + if A[Q]==Z:A['release']='2.0.0' + if A[Q]==u: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[P]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[D]and not A[C].endswith(W):A[C]=A[C]+W + A[L]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A +def A6(version): + A=version;B=J.join([c(A)for A in A[:3]]) + if P(A)>3 and A[3]:B+=V+A[3] + return B +def A7(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except O:A.warning('BOARDNAME not found');C=B return C -def h(filename): +def get_root(): + try:A=os.getcwd() + except(D,I):A=J + B=A + for B in[A,'/sd','/flash',G,J]: + try:C=os.stat(B);break + except D:continue + return B +def Z(filename): try: - if os.stat(filename)[0]>>14:return M - return F - except A:return F -def i():sys.exit(1) + if os.stat(filename)[0]>>14:return R + return H + except D:return H +def l():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): - path=C - if L(sys.argv)==3: + path=B + if P(sys.argv)==3: A=sys.argv[1].lower() if A in('--path','-p'):path=sys.argv[2] - else:i() - elif L(sys.argv)==2:i() + else:l() + elif P(sys.argv)==2:l() return path -def j(): - try:A=bytes('abc',encoding='utf8');B=j.__module__;return F - except(k,H):return M -def main(): - K='failed';G='modulelist.done';import machine as O - try:C=I(G,'r+b');N=M - except A:C=I(G,'w+b');N=F - stubber=Stubber(path=read_path()) - if not N:stubber.clean() - y(stubber);D={} - try: - with I(G)as C: - for E in C.read().split('\n'): - E=E.strip();B.collect() - if L(E)>0:P,Q=E.split('=',1);D[P]=Q - except(A,SyntaxError):pass - B.collect();R=[A for A in stubber.modules if A not in D.keys()];B.collect() - for H in R: - J=F - try:J=stubber.create_one_stub(H) - except l:O.reset() - B.collect();D[H]=W(stubber._report[-1]if J else K) - with I(G,'a')as C:C.write('{}={}\n'.format(H,'ok'if J else K)) - if D:stubber._report=[A for(B,A)in D.items()if A!=K];stubber.report() -def y(stubber): - E='/modulelist.txt';stubber.modules=[] - for D in e: +def m(): + try:A=bytes('abc',encoding='utf8');B=m.__module__;return H + except(n,I):return R +a='modulelist.done' +def A8(skip=0): + for E in A5: + B=E+'/modulelist.txt' + if not Z(B):continue try: - with I(D+E)as F: - N('DEBUG: list of modules: '+D+E) - for C in F.read().split('\n'): - C=C.strip() - if L(C)>0 and C[0]!='#':stubber.modules.append(C) - B.collect();break - except A:pass - if not stubber.modules:stubber.modules=[X];_log.warn('Could not find modulelist.txt, using default modules') - B.collect() -if __name__=='__main__'or j(): - try:z=logging.getLogger(v);logging.basicConfig(level=logging.INFO) - except n:pass - if not h('no_auto_stubber.txt'): - try:B.threshold(4*1024);B.enable() + with M(B)as F: + C=0 + while R: + A=F.readline().strip() + if not A:break + if P(A)>0 and A[0]=='#':continue + C+=1 + if C bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + log.trace("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -191,7 +243,7 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) @@ -201,11 +253,12 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # lets not try - most times it does not work anyway + # try: + # del sys.modules[module_name] + # except KeyError: + # log.warning("could not del sys.modules[{}]".format(module_name)) gc.collect() return True @@ -213,14 +266,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -228,11 +281,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + log.trace("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -251,11 +309,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -269,7 +327,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -285,7 +343,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -304,27 +362,29 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ["object", "set", "frozenset", "Pin", "FileIO"]: # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + s = "{0}{1} : {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -340,7 +400,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -360,9 +420,9 @@ def clean(self, path: str = None): # type: ignore def report(self, filename: str = "modules.json"): "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) + log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + log.info("Report file: {}".format(f_name)) gc.collect() try: # write json by node to reduce memory requirements @@ -374,9 +434,9 @@ def report(self, filename: str = "modules.json"): first = False self.write_json_end(f) used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) + log.trace("Memory used: {0} Kb".format(used // 1024)) except OSError: - self.log.error("Failed to create the report.") + log.error("Failed to create the report.") def write_json_header(self, f): f.write("{") @@ -418,12 +478,21 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] @@ -434,7 +503,7 @@ def _info(): # type:() -> dict[str, str] "build": "", "ver": "", "port": sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", @@ -448,13 +517,14 @@ def _info(): # type:() -> dict[str, str] elif info["port"] == "linux": info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( sys.implementation._mpy if "_mpy" in dir(sys.implementation) @@ -464,40 +534,27 @@ def _info(): # type:() -> dict[str, str] ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -519,13 +576,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -549,25 +607,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -668,12 +732,6 @@ def main(): if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/src/stubber/board/createstubs_lvgl_min.py b/src/stubber/board/createstubs_lvgl_min.py index 69b505b09..827b5fb86 100644 --- a/src/stubber/board/createstubs_lvgl_min.py +++ b/src/stubber/board/createstubs_lvgl_min.py @@ -1,278 +1,741 @@ -t='stubber' -s='{}/{}' -r='method' -q='function' -p='bool' -o='str' -n='float' -m='int' -l='micropython' -k=Exception -j=NameError -i=sorted -h=NotImplementedError -Z=',\n' -Y='dict' -X='list' -W='tuple' -V=open -U=repr -S='_' -R=len -Q=KeyError -P=IndexError -O=dir -M=print -N=ImportError -K=True -L='family' -J='board' -I='.' -H=AttributeError -A=False -G='/' -E=None -D='version' -F=OSError -B='' -import gc as C,os,sys -from ujson import dumps as a -try:from machine import reset -except N:pass -try:from collections import OrderedDict as b -except N:from ucollections import OrderedDict as b -__version__='v1.16.2' -u=2 -v=2 -w=[I,'/lib','/sd/lib','/flash/lib','lib'] +""" +Create stubs for the lvgl modules on a MicroPython board. + +Note that the stubs can be very large, and it may be best to directly store them on an SD card if your device supports this. + +This variant was generated from createstubs.py by micropython-stubber v1.16.3 +""" +# Copyright (c) 2019-2023 Jos Verlinde + +import gc +import os +import sys from time import sleep + +try: + from ujson import dumps +except: + from json import dumps + +try: + from machine import reset # type: ignore +except ImportError: + pass + +try: + from collections import OrderedDict +except ImportError: + from ucollections import OrderedDict # type: ignore + +__version__ = "v1.16.3" +ENOENT = 2 +_MAX_CLASS_LEVEL = 2 # Max class nesting +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + DEBUG = 10 + TRACE = 15 + INFO = 20 + WARNING = 30 + ERROR = 40 + CRITICAL = 50 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + pass + + def trace(self, msg): + if self.level <= logging.TRACE: + self.prnt("TRACE :", msg) + + def debug(self, msg): + if self.level <= logging.DEBUG: + self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + def critical(self, msg): + if self.level <= logging.CRITICAL: + self.prnt("CRIT :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.TRACE) +# logging.basicConfig(level=logging.DEBUG) + + class Stubber: - def __init__(A,path=E,firmware_id=E): - B=firmware_id - try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise h('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();C.collect() - if B:A._fwid=B.lower() - elif A.info[L]==l:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=C.mem_free() - if path: - if path.endswith(G):path=path[:-1] - else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:c(path+G) - except F:M('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] - def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in O(I): - if A.startswith(S)and not A in L.modules:continue - try: - E=getattr(I,A) - try:F=U(type(E)).split("'")[1] - except P:F=B - if F in{m,n,o,p,W,X,Y}:G=1 - elif F in{q,r}:G=2 - elif F in'class':G=3 - else:G=4 - D.append((A,U(E),U(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=i([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);C.collect();return D,J - def add_modules(A,modules):A.modules=i(set(A.modules)|set(modules)) - def create_all_stubs(A): - C.collect() - for B in A.modules:A.create_one_stub(B) - def create_one_stub(B,module_name): - D=module_name - if D in B.problematic:return A - if D in B.excluded:return A - H='{}/{}.py'.format(B.path,D.replace(I,G));C.collect();E=A - try:E=B.create_module_stub(D,H) - except F:return A - C.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(I,S)+'.py';H=J.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,I) - L=E - try:L=__import__(D,E,E,'*');T=C.mem_free() - except N:return A - c(H) - with V(H,'w')as M:P=str(J.info).replace('OrderedDict(',B).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);M.write(R);M.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(M,L,D,B) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del L - except(F,Q):pass - try:del sys.modules[D] - except Q:pass - C.collect();return K - def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';P=in_class;O=object_expr;N='Exception';H=fp;D=indent;C.collect() - if O in K.problematic:return - S,L=K.get_obj_attributes(O) - if L:M(L) - for(E,J,G,T,f)in S: - if E in['classmethod','staticmethod','BaseException',N]:continue - if E[0].isdigit():continue - if G==""and R(D)<=v*4: - U=B;V=E.endswith(N)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=N - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',P+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[r,q,'closure']): - Z=b;a=B - if P>0:a='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,Z) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,a,Z) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith("5:A[F]=B - if A[D]==B and sys.platform not in(h,g): - try:m=os.uname();A[D]=m.release - except(P,H,TypeError):pass - for(n,o,p)in[(i,i,'const'),(j,j,'FAT'),(k,'pybricks.hubs','EV3Brick')]: - try:q=__import__(o,E,E,p);A[L]=n;del q;break - except(N,Q):pass - if A[L]==k:A['release']='2.0.0' - if A[L]==l: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if K in A and A[K]: - V=int(A[K]);Y=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][V>>10] - if Y:A[c]=Y - A[K]='v{}.{}'.format(V&255,V>>8&3) - A[Z]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def d(info,board_descr,filename): - with V(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return K - return A -def get_root(): - try:A=os.getcwd() - except(F,H):A=I - B=A - for B in[A,'/sd','/flash',G,I]: - try:C=os.stat(B);break - except F:continue - return B -def e(filename): - try: - if os.stat(filename)[0]>>14:return K - return A - except F:return A -def f():sys.exit(1) -def read_path(): - path=B - if R(sys.argv)==3: - A=sys.argv[1].lower() - if A in('--path','-p'):path=sys.argv[2] - else:f() - elif R(sys.argv)==2:f() - return path -def g(): - try:B=bytes('abc',encoding='utf8');C=g.__module__;return A - except(h,H):return K + "Generate stubs for modules in firmware" + + def __init__(self, path: str = None, firmware_id: str = None): # type: ignore + try: + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore + raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") + except AttributeError: + pass + self._report = [] # type: list[str] + self.info = _info() + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) + gc.collect() + if firmware_id: + self._fwid = firmware_id.lower() + else: + if self.info["family"] == "micropython": + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") + else: + self._fwid = "{family}-v{version}-{port}".format(**self.info) + self._start_free = gc.mem_free() # type: ignore + + if path: + if path.endswith("/"): + path = path[:-1] + else: + path = get_root() + + self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") + log.debug(self.path) + try: + ensure_folder(path + "/") + except OSError: + print("error creating stub folder {}".format(path)) + self.problematic = [ + "upip", + "upysh", + "webrepl_setup", + "http_client", + "http_client_ssl", + "http_server", + "http_server_ssl", + ] + self.excluded = [ + "webrepl", + "_webrepl", + "port_diag", + "example_sub_led.py", + "example_pub_button.py", + ] + # there is no option to discover modules from micropython, list is read from an external file. + self.modules = [] # type: list[str] + + def get_obj_attributes(self, item_instance: object): + "extract information of the objects members and attributes" + # name_, repr_(value), type as text, item_instance + _result = [] + _errors = [] + log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + for name in dir(item_instance): + if name.startswith("_") and not name in self.modules: + continue + log.debug("get attribute {}".format(name)) + try: + val = getattr(item_instance, name) + # name , item_repr(value) , type as text, item_instance, order + log.debug("attribute {}:{}".format(name, val)) + try: + type_text = repr(type(val)).split("'")[1] + except IndexError: + type_text = "" + if type_text in {"int", "float", "str", "bool", "tuple", "list", "dict"}: + order = 1 + elif type_text in {"function", "method"}: + order = 2 + elif type_text in ("class"): + order = 3 + else: + order = 4 + _result.append((name, repr(val), repr(type(val)), val, order)) + except AttributeError as e: + _errors.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(name, item_instance, e)) + except MemoryError as e: + # print("MemoryError: {}".format(e)) + sleep(1) + reset() + + # remove internal __ + # _result = sorted([i for i in _result if not (i[0].startswith("_"))], key=lambda x: x[4]) + _result = sorted([i for i in _result if not (i[0].startswith("__"))], key=lambda x: x[4]) + gc.collect() + return _result, _errors + + def add_modules(self, modules): + "Add additional modules to be exported" + self.modules = sorted(set(self.modules) | set(modules)) + + def create_all_stubs(self): + "Create stubs for all configured modules" + log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + gc.collect() + for module_name in self.modules: + self.create_one_stub(module_name) + log.info("Finally done") + + def create_one_stub(self, module_name: str): + if module_name in self.problematic: + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + return False + if module_name in self.excluded: + log.warning("Skip module: {:<25} : Excluded".format(module_name)) + return False + + file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + gc.collect() + result = False + try: + result = self.create_module_stub(module_name, file_name) + except OSError: + return False + gc.collect() + return result + + def create_module_stub(self, module_name: str, file_name: str = None) -> bool: # type: ignore + """Create a Stub of a single python module + + Args: + - module_name (str): name of the module to document. This module will be imported. + - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + """ + if file_name is None: + fname = module_name.replace(".", "_") + ".py" + file_name = self.path + "/" + fname + else: + fname = file_name.split("/")[-1] + + if "/" in module_name: + # for nested modules + module_name = module_name.replace("/", ".") + + # import the module (as new_module) to examine it + new_module = None + try: + new_module = __import__(module_name, None, None, ("*")) + m1 = gc.mem_free() # type: ignore + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + + except ImportError: + log.trace("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + return False + + # Start a new file + ensure_folder(file_name) + with open(file_name, "w") as fp: + # todo: improve header + info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") + s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) + fp.write(s) + fp.write("from __future__ import annotations\nfrom typing import Any\nfrom _typeshed import Incomplete\n\n") + self.write_object_stub(fp, new_module, module_name, "") + + self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + + if module_name not in {"os", "sys", "logging", "gc"}: + # try to unload the module unless we use it + try: + del new_module + except (OSError, KeyError): # lgtm [py/unreachable-statement] + log.warning("could not del new_module") + # lets not try - most times it does not work anyway + # try: + # del sys.modules[module_name] + # except KeyError: + # log.warning("could not del sys.modules[{}]".format(module_name)) + gc.collect() + return True + + def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, in_class: int = 0): + "Write a module/object stub to an open file. Can be called recursive." + gc.collect() + if object_expr in self.problematic: + log.warning("SKIPPING problematic module:{}".format(object_expr)) + return + + # log.debug("DUMP : {}".format(object_expr)) + items, errors = self.get_obj_attributes(object_expr) + + if errors: + print(errors) + + for item_name, item_repr, item_type_txt, item_instance, _ in items: + # name_, repr_(value), type as text, item_instance, order + if item_name in ["classmethod", "staticmethod", "BaseException", "Exception"]: + # do not create stubs for these primitives + continue + if item_name[0].isdigit(): + log.warning("NameError: invalid name {}".format(item_name)) + continue + # Class expansion only on first 3 levels (bit of a hack) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + log.trace("{0}class {1}:".format(indent, item_name)) + superclass = "" + is_exception = ( + item_name.endswith("Exception") + or item_name.endswith("Error") + or item_name + in [ + "KeyboardInterrupt", + "StopIteration", + "SystemExit", + ] + ) + if is_exception: + superclass = "Exception" + s = "\n{}class {}({}):\n".format(indent, item_name, superclass) + # s += indent + " ''\n" + if is_exception: + s += indent + " ...\n" + fp.write(s) + continue + # write classdef + fp.write(s) + # first write the class literals and methods + log.debug("# recursion over class {0}".format(item_name)) + self.write_object_stub( + fp, + item_instance, + "{0}.{1}".format(obj_name, item_name), + indent + " ", + in_class + 1, + ) + # end with the __init__ method to make sure that the literals are defined + # Add __init__ + s = indent + " def __init__(self, *argv, **kwargs) -> None:\n" + s += indent + " ...\n\n" + fp.write(s) + elif any(word in item_type_txt for word in ["method", "function", "closure"]): + log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # module Function or class method + # will accept any number of params + # return type Any/Incomplete + ret = "Incomplete" + first = "" + # Self parameter only on class methods/functions + if in_class > 0: + first = "self, " + # class method - add function decoration + if "bound_method" in item_type_txt or "bound_method" in item_repr: + s = "{}@classmethod\n".format(indent) + "{}def {}(cls, *args, **kwargs) -> {}:\n".format(indent, item_name, ret) + else: + s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) + s += indent + " ...\n\n" + fp.write(s) + log.debug("\n" + s) + elif item_type_txt == "": + # Skip imported modules + # fp.write("# import {}\n".format(item_name)) + pass + + elif item_type_txt.startswith(" dict[str, str] + info = OrderedDict( + { + "family": sys.implementation.name, + "version": "", + "build": "", + "ver": "", + "port": sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "UNKNOWN", + "cpu": "", + "mpy": "", + "arch": "", + } + ) + # change port names to be consistent with the repo + if info["port"].startswith("pyb"): + info["port"] = "stm32" + elif info["port"] == "win32": + info["port"] = "windows" + elif info["port"] == "linux": + info["port"] = "unix" + try: + info["version"] = version_str(sys.implementation.version) # type: ignore + except AttributeError: + pass + try: + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() + info["mpy"] = ( + sys.implementation._mpy + if "_mpy" in dir(sys.implementation) + else sys.implementation.mpy + if "mpy" in dir(sys.implementation) + else "" + ) + except (AttributeError, IndexError): + pass + info["board"] = get_boardname() + + try: + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): + pass + # avoid build hashes + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" + + if info["version"] == "" and sys.platform not in ("unix", "win32"): + try: + u = os.uname() # type: ignore + info["version"] = u.release + except (IndexError, AttributeError, TypeError): + pass + # detect families + for fam_name, mod_name, mod_thing in [ + ("pycopy", "pycopy", "const"), + ("pycom", "pycom", "FAT"), + ("ev3-pybricks", "pybricks.hubs", "EV3Brick"), + ]: + try: + _t = __import__(mod_name, None, None, (mod_thing)) + info["family"] = fam_name + del _t + break + except (ImportError, KeyError): + pass + + if info["family"] == "ev3-pybricks": + info["release"] = "2.0.0" + + if info["family"] == "micropython": + info["version"] + if ( + info["version"] + and info["version"].endswith(".0") + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] <= "1.19.9" + ): + # versions from 1.10.0 to 1.20.0 do not have a micro .0 + info["version"] = info["version"][:-2] + + # spell-checker: disable + if "mpy" in info and info["mpy"]: # mpy on some v1.11+ builds + sys_mpy = int(info["mpy"]) + # .mpy architecture + arch = [ + None, + "x86", + "x64", + "armv6", + "armv6m", + "armv7m", + "armv7em", + "armv7emsp", + "armv7emdp", + "xtensa", + "xtensawin", + ][sys_mpy >> 10] + if arch: + info["arch"] = arch + # .mpy version.minor + info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" + # simple to use version[-build] string + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" + + return info + + +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME + + +def get_root() -> str: # sourcery skip: use-assigned-variable + "Determine the root folder of the device" + try: + c = os.getcwd() + except (OSError, AttributeError): + # unix port + c = "." + r = c + for r in [c, "/sd", "/flash", "/", "."]: + try: + _ = os.stat(r) + break + except OSError: + continue + return r + + +def file_exists(filename: str): + try: + if os.stat(filename)[0] >> 14: + return True + return False + except OSError: + return False + + +def show_help(): + # print("-p, --path path to store the stubs in, defaults to '.'") + sys.exit(1) + + +def read_path() -> str: + "get --path from cmdline. [unix/win]" + path = "" + if len(sys.argv) == 3: + cmd = (sys.argv[1]).lower() + if cmd in ("--path", "-p"): + path = sys.argv[2] + else: + show_help() + elif len(sys.argv) == 2: + show_help() + return path + + +def is_micropython() -> bool: + "runtime test to determine full or micropython" + # pylint: disable=unused-variable,eval-used + try: + # either test should fail on micropython + # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces + # Micropython : SyntaxError + # a = eval("1and 0") # lgtm [py/unused-local-variable] + # Eval blocks some minification aspects + + # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented + # Micropython: NotImplementedError + b = bytes("abc", encoding="utf8") # type: ignore # lgtm [py/unused-local-variable] + + # c) https://docs.micropython.org/en/latest/genrst/core_language.html#function-objects-do-not-have-the-module-attribute + # Micropython: AttributeError + c = is_micropython.__module__ # type: ignore # lgtm [py/unused-local-variable] + return False + except (NotImplementedError, AttributeError): + return True + + def main(): - D='lvgl' - try:import lvgl as A - except k:return - B=D - try:B='lvgl-{0}_{1}_{2}-{3}-{4}'.format(A.version_major(),A.version_minor(),A.version_patch(),A.version_info(),sys.platform) - except k:B='lvgl-{0}_{1}_{2}_{3}-{4}'.format(8,1,0,'dev',sys.platform) - finally:stubber=Stubber(firmware_id=B) - stubber.clean();stubber.modules=['io','lodepng','rtch',D];C.collect();stubber.create_all_stubs();stubber.report() -if __name__=='__main__'or g(): - try:x=logging.getLogger(t);logging.basicConfig(level=logging.INFO) - except j:pass - if not e('no_auto_stubber.txt'): - try:C.threshold(4*1024);C.enable() - except BaseException:pass - main() \ No newline at end of file + try: + import lvgl # type: ignore + except Exception: + # print("\n\nNOTE: The `lvgl` module could not be found on this firmware\n\n") + return + # Specify firmware name & version + fw_id = "lvgl" + try: + fw_id = "lvgl-{0}_{1}_{2}-{3}-{4}".format( + lvgl.version_major(), + lvgl.version_minor(), + lvgl.version_patch(), + lvgl.version_info(), + sys.platform, + ) + except Exception: + fw_id = "lvgl-{0}_{1}_{2}_{3}-{4}".format(8, 1, 0, "dev", sys.platform) + finally: + stubber = Stubber(firmware_id=fw_id) + stubber.clean() + # modules to stub : only lvgl specifics + stubber.modules = ["io", "lodepng", "rtch", "lvgl"] # spell-checker: enable + + gc.collect() + + stubber.create_all_stubs() + stubber.report() + + +if __name__ == "__main__" or is_micropython(): + if not file_exists("no_auto_stubber.txt"): + try: + gc.threshold(4 * 1024) # type: ignore + gc.enable() + except BaseException: + pass + main() diff --git a/src/stubber/board/createstubs_lvgl_mpy.mpy b/src/stubber/board/createstubs_lvgl_mpy.mpy index b2bc3e029..a2ddea266 100644 Binary files a/src/stubber/board/createstubs_lvgl_mpy.mpy and b/src/stubber/board/createstubs_lvgl_mpy.mpy differ diff --git a/src/stubber/board/createstubs_mem.py b/src/stubber/board/createstubs_mem.py index 262d5d37c..9a4349297 100644 --- a/src/stubber/board/createstubs_mem.py +++ b/src/stubber/board/createstubs_mem.py @@ -9,16 +9,19 @@ - cross compilation, using mpy-cross, to avoid the compilation step on the micropython device -This variant was generated from createstubs.py by micropython-stubber v1.16.2 +This variant was generated from createstubs.py by micropython-stubber v1.17.0 """ # Copyright (c) 2019-2023 Jos Verlinde import gc -import logging import os import sys +from time import sleep -from ujson import dumps +try: + from ujson import dumps +except: + from json import dumps try: from machine import reset # type: ignore @@ -30,11 +33,49 @@ except ImportError: from ucollections import OrderedDict # type: ignore -__version__ = "v1.16.2" +__version__ = "v1.17.0" ENOENT = 2 _MAX_CLASS_LEVEL = 2 # Max class nesting -LIBS = [".", "/lib", "/sd/lib", "/flash/lib", "lib"] -from time import sleep +LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."] + + +# our own logging module to avoid dependency on and interfering with logging module +class logging: + # DEBUG = 10 + INFO = 20 + WARNING = 30 + ERROR = 40 + level = INFO + prnt = print + + @staticmethod + def getLogger(name): + return logging() + + @classmethod + def basicConfig(cls, level): + cls.level = level + + # def debug(self, msg): + # if self.level <= logging.DEBUG: + # self.prnt("DEBUG :", msg) + + def info(self, msg): + if self.level <= logging.INFO: + self.prnt("INFO :", msg) + + def warning(self, msg): + if self.level <= logging.WARNING: + self.prnt("WARN :", msg) + + def error(self, msg): + if self.level <= logging.ERROR: + self.prnt("ERROR :", msg) + + +log = logging.getLogger("stubber") +logging.basicConfig(level=logging.INFO) +# logging.basicConfig(level=logging.DEBUG) class Stubber: @@ -42,24 +83,21 @@ class Stubber: def __init__(self, path: str = None, firmware_id: str = None): # type: ignore try: - if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": + if os.uname().release == "1.13.0" and os.uname().version < "v1.13-103": # type: ignore raise NotImplementedError("MicroPython 1.13.0 cannot be stubbed") except AttributeError: pass - self.log = None - self.log = logging.getLogger("stubber") - self._report = [] # type: list[str] self.info = _info() - self.log.info("Port: {}".format(self.info["port"])) - self.log.info("Board: {}".format(self.info["board"])) + log.info("Port: {}".format(self.info["port"])) + log.info("Board: {}".format(self.info["board"])) gc.collect() if firmware_id: self._fwid = firmware_id.lower() else: if self.info["family"] == "micropython": - self._fwid = "{family}-{ver}-{port}-{board}".format(**self.info) + self._fwid = "{family}-v{version}-{port}-{board}".format(**self.info).rstrip("-") else: - self._fwid = "{family}-{ver}-{port}".format(**self.info) + self._fwid = "{family}-v{version}-{port}".format(**self.info) self._start_free = gc.mem_free() # type: ignore if path: @@ -69,11 +107,11 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore path = get_root() self.path = "{}/stubs/{}".format(path, self.flat_fwid).replace("//", "/") - self.log.debug(self.path) + # log.debug(self.path) try: ensure_folder(path + "/") except OSError: - self.log.error("error creating stub folder {}".format(path)) + log.error("error creating stub folder {}".format(path)) self.problematic = [ "upip", "upysh", @@ -92,20 +130,23 @@ def __init__(self, path: str = None, firmware_id: str = None): # type: ignore ] # there is no option to discover modules from micropython, list is read from an external file. self.modules = [] # type: list[str] + self._json_name = None + self._json_first = False def get_obj_attributes(self, item_instance: object): "extract information of the objects members and attributes" # name_, repr_(value), type as text, item_instance _result = [] _errors = [] - self.log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) + # log.debug("get attributes {} {}".format(repr(item_instance), item_instance)) for name in dir(item_instance): - if name.startswith("_") and not name in self.modules: + if name.startswith("__") and not name in self.modules: continue - self.log.debug("get attribute {}".format(name)) + # log.debug("get attribute {}".format(name)) try: val = getattr(item_instance, name) # name , item_repr(value) , type as text, item_instance, order + # log.debug("attribute {}:{}".format(name, val)) try: type_text = repr(type(val)).split("'")[1] except IndexError: @@ -138,21 +179,23 @@ def add_modules(self, modules): def create_all_stubs(self): "Create stubs for all configured modules" - self.log.info("Start micropython-stubber v{} on {}".format(__version__, self._fwid)) + log.info("Start micropython-stubber {} on {}".format(__version__, self._fwid)) + self.report_start() gc.collect() for module_name in self.modules: self.create_one_stub(module_name) - self.log.info("Finally done") + self.report_end() + log.info("Finally done") def create_one_stub(self, module_name: str): if module_name in self.problematic: - self.log.warning("Skip module: {:<25} : Known problematic".format(module_name)) + log.warning("Skip module: {:<25} : Known problematic".format(module_name)) return False if module_name in self.excluded: - self.log.warning("Skip module: {:<25} : Excluded".format(module_name)) + log.warning("Skip module: {:<25} : Excluded".format(module_name)) return False - file_name = "{}/{}.py".format(self.path, module_name.replace(".", "/")) + file_name = "{}/{}.pyi".format(self.path, module_name.replace(".", "/")) gc.collect() result = False try: @@ -167,10 +210,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: Args: - module_name (str): name of the module to document. This module will be imported. - - file_name (Optional[str]): the 'path/filename.py' to write to. If omitted will be created based on the module name. + - file_name (Optional[str]): the 'path/filename.pyi' to write to. If omitted will be created based on the module name. """ if file_name is None: - fname = module_name.replace(".", "_") + ".py" + fname = module_name.replace(".", "_") + ".pyi" file_name = self.path + "/" + fname else: fname = file_name.split("/")[-1] @@ -184,10 +227,10 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: try: new_module = __import__(module_name, None, None, ("*")) m1 = gc.mem_free() # type: ignore - self.log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) + log.info("Stub module: {:<25} to file: {:<70} mem:{:>5}".format(module_name, fname, m1)) except ImportError: - self.log.warning("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) + # log.debug("Skip module: {:<25} {:<79}".format(module_name, "Module not found.")) return False # Start a new file @@ -197,21 +240,18 @@ def create_module_stub(self, module_name: str, file_name: str = None) -> bool: info_ = str(self.info).replace("OrderedDict(", "").replace("})", "}") s = '"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(module_name, self._fwid, info_, __version__) fp.write(s) - fp.write("from typing import Any\nfrom _typeshed import Incomplete\n\n") + fp.write("from __future__ import annotations\nfrom typing import Any, Generator\nfrom _typeshed import Incomplete\n\n") self.write_object_stub(fp, new_module, module_name, "") - self._report.append('{{"module": "{}", "file": "{}"}}'.format(module_name, file_name.replace("\\", "/"))) + self.report_add(module_name, file_name) if module_name not in {"os", "sys", "logging", "gc"}: # try to unload the module unless we use it try: del new_module except (OSError, KeyError): # lgtm [py/unreachable-statement] - self.log.warning("could not del new_module") - try: - del sys.modules[module_name] - except KeyError: - self.log.debug("could not del sys.modules[{}]".format(module_name)) + log.warning("could not del new_module") + # do not try to delete from sys.modules - most times it does not work anyway gc.collect() return True @@ -219,14 +259,14 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, "Write a module/object stub to an open file. Can be called recursive." gc.collect() if object_expr in self.problematic: - self.log.warning("SKIPPING problematic module:{}".format(object_expr)) + log.warning("SKIPPING problematic module:{}".format(object_expr)) return - # self.log.debug("DUMP : {}".format(object_expr)) + # # log.debug("DUMP : {}".format(object_expr)) items, errors = self.get_obj_attributes(object_expr) if errors: - self.log.error(errors) + log.error(errors) for item_name, item_repr, item_type_txt, item_instance, _ in items: # name_, repr_(value), type as text, item_instance, order @@ -234,11 +274,16 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, # do not create stubs for these primitives continue if item_name[0].isdigit(): - self.log.warning("NameError: invalid name {}".format(item_name)) + log.warning("NameError: invalid name {}".format(item_name)) continue # Class expansion only on first 3 levels (bit of a hack) - if item_type_txt == "" and len(indent) <= _MAX_CLASS_LEVEL * 4: - self.log.debug("{0}class {1}:".format(indent, item_name)) + if ( + item_type_txt == "" + and len(indent) <= _MAX_CLASS_LEVEL * 4 + # and not obj_name.endswith(".Pin") + # avoid expansion of Pin.cpu / Pin.board to avoid crashes on most platforms + ): + # log.debug("{0}class {1}:".format(indent, item_name)) superclass = "" is_exception = ( item_name.endswith("Exception") @@ -257,11 +302,11 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, if is_exception: s += indent + " ...\n" fp.write(s) - return + continue # write classdef fp.write(s) # first write the class literals and methods - self.log.debug("# recursion over class {0}".format(item_name)) + # log.debug("# recursion over class {0}".format(item_name)) self.write_object_stub( fp, item_instance, @@ -275,7 +320,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s += indent + " ...\n\n" fp.write(s) elif any(word in item_type_txt for word in ["method", "function", "closure"]): - self.log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) + # log.debug("# def {1} function/method/closure, type = '{0}'".format(item_type_txt, item_name)) # module Function or class method # will accept any number of params # return type Any/Incomplete @@ -291,7 +336,7 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, s = "{}def {}({}*args, **kwargs) -> {}:\n".format(indent, item_name, first, ret) s += indent + " ...\n\n" fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) elif item_type_txt == "": # Skip imported modules # fp.write("# import {}\n".format(item_name)) @@ -301,36 +346,42 @@ def write_object_stub(self, fp, object_expr: object, obj_name: str, indent: str, t = item_type_txt[8:-2] s = "" - if t in ["str", "int", "float", "bool", "bytearray", "bytes"]: + if t in ("str", "int", "float", "bool", "bytearray", "bytes"): # known type: use actual value - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) - elif t in ["dict", "list", "tuple"]: + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, item_repr, t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, item_repr, t) + elif t in ("dict", "list", "tuple"): # dict, list , tuple: use empty value ev = {"dict": "{}", "list": "[]", "tuple": "()"} - s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + # s = "{0}{1} = {2} # type: {3}\n".format(indent, item_name, ev[t], t) + s = "{0}{1}: {3} = {2}\n".format(indent, item_name, ev[t], t) else: # something else - if t not in ["object", "set", "frozenset"]: - # Possibly default others to item_instance object ? + if t in ("object", "set", "frozenset", "Pin", "generator"): # "FileIO" # https://docs.python.org/3/tutorial/classes.html#item_instance-objects + # use these types for the attribute + if t == "generator": + t = "Generator" + s = "{0}{1}: {2} ## = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + else: + # Requires Python 3.6 syntax, which is OK for the stubs/pyi t = "Incomplete" - # Requires Python 3.6 syntax, which is OK for the stubs/pyi - s = "{0}{1} : {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) + s = "{0}{1}: {2} ## {3} = {4}\n".format(indent, item_name, t, item_type_txt, item_repr) fp.write(s) - self.log.debug("\n" + s) + # log.debug("\n" + s) else: # keep only the name - self.log.debug("# all other, type = '{0}'".format(item_type_txt)) + # log.debug("# all other, type = '{0}'".format(item_type_txt)) fp.write("# all other, type = '{0}'\n".format(item_type_txt)) fp.write(indent + item_name + " # type: Incomplete\n") - del items - del errors - try: - del item_name, item_repr, item_type_txt, item_instance # type: ignore - except (OSError, KeyError, NameError): - pass + # del items + # del errors + # try: + # del item_name, item_repr, item_type_txt, item_instance # type: ignore + # except (OSError, KeyError, NameError): + # pass @property def flat_fwid(self): @@ -346,7 +397,7 @@ def clean(self, path: str = None): # type: ignore "Remove all files from the stub folder" if path is None: path = self.path - self.log.info("Clean/remove files in folder: {}".format(path)) + log.info("Clean/remove files in folder: {}".format(path)) try: os.stat(path) # TEMP workaround mpremote listdir bug - items = os.listdir(path) @@ -364,41 +415,53 @@ def clean(self, path: str = None): # type: ignore except OSError: pass - def report(self, filename: str = "modules.json"): - "create json with list of exported modules" - self.log.info("Created stubs for {} modules on board {}\nPath: {}".format(len(self._report), self._fwid, self.path)) - f_name = "{}/{}".format(self.path, filename) - self.log.info("Report file: {}".format(f_name)) + def report_start(self, filename: str = "modules.json"): + """Start a report of the modules that have been stubbed + "create json with list of exported modules""" + self._json_name = "{}/{}".format(self.path, filename) + self._json_first = True + ensure_folder(self._json_name) + log.info("Report file: {}".format(self._json_name)) gc.collect() try: # write json by node to reduce memory requirements - with open(f_name, "w") as f: - self.write_json_header(f) - first = True - for n in self._report: - self.write_json_node(f, n, first) - first = False - self.write_json_end(f) - used = self._start_free - gc.mem_free() # type: ignore - self.log.info("Memory used: {0} Kb".format(used // 1024)) - except OSError: - self.log.error("Failed to create the report.") - - def write_json_header(self, f): - f.write("{") - f.write(dumps({"firmware": self.info})[1:-1]) - f.write(",\n") - f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) - f.write(",\n") - f.write('"modules" :[\n') + with open(self._json_name, "w") as f: + f.write("{") + f.write(dumps({"firmware": self.info})[1:-1]) + f.write(",\n") + f.write(dumps({"stubber": {"version": __version__}, "stubtype": "firmware"})[1:-1]) + f.write(",\n") + f.write('"modules" :[\n') + + except OSError as e: + log.error("Failed to create the report.") + self._json_name = None + raise e + + def report_add(self, module_name: str, stub_file: str): + "Add a module to the report" + # write json by node to reduce memory requirements + if not self._json_name: + raise Exception("No report file") + try: + with open(self._json_name, "a") as f: + if not self._json_first: + f.write(",\n") + else: + self._json_first = False + line = '{{"module": "{}", "file": "{}"}}'.format(module_name, stub_file.replace("\\", "/")) + f.write(line) - def write_json_node(self, f, n, first): - if not first: - f.write(",\n") - f.write(n) + except OSError: + log.error("Failed to create the report.") - def write_json_end(self, f): - f.write("\n]}") + def report_end(self): + if not self._json_name: + raise Exception("No report file") + with open(self._json_name, "a") as f: + f.write("\n]}") + # is used as sucess indicator + log.info("Path: {}".format(self.path)) def ensure_folder(path: str): @@ -424,23 +487,32 @@ def ensure_folder(path: str): def _build(s): - # extract a build nr from a string + # extract build from sys.version or os.uname().version if available + # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f' + # sys.implementation.version: 'v1.13-103-gb137d064e' if not s: return "" - if " on " in s: - s = s.split(" on ", 1)[0] - return s.split("-")[1] if "-" in s else "" + s = s.split(" on ", 1)[0] if " on " in s else s + if s.startswith("v"): + if not "-" in s: + return "" + b = s.split("-")[1] + return b + if not "-preview" in s: + return "" + b = s.split("-preview")[1].split(".")[1] + return b def _info(): # type:() -> dict[str, str] info = OrderedDict( { - "family": sys.implementation.name, + "family": sys.implementation.name, # type: ignore "version": "", "build": "", "ver": "", "port": sys.platform, # port: esp32 / win32 / linux / stm32 - "board": "GENERIC", + "board": "UNKNOWN", "cpu": "", "mpy": "", "arch": "", @@ -454,56 +526,44 @@ def _info(): # type:() -> dict[str, str] elif info["port"] == "linux": info["port"] = "unix" try: - info["version"] = ".".join([str(n) for n in sys.implementation.version]) + info["version"] = version_str(sys.implementation.version) # type: ignore except AttributeError: pass try: - machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine - info["board"] = machine.strip() - info["cpu"] = machine.split("with")[1].strip() + _machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine # type: ignore + # info["board"] = "with".join(_machine.split("with")[:-1]).strip() + info["board"] = _machine + info["cpu"] = _machine.split("with")[-1].strip() info["mpy"] = ( - sys.implementation._mpy + sys.implementation._mpy # type: ignore if "_mpy" in dir(sys.implementation) - else sys.implementation.mpy + else sys.implementation.mpy # type: ignore if "mpy" in dir(sys.implementation) else "" ) except (AttributeError, IndexError): pass - gc.collect() - for filename in [d + "/board_info.csv" for d in LIBS]: - # print("look up the board name in the file", filename) - if file_exists(filename): - # print("Found board info file: {}".format(filename)) - b = info["board"].strip() - if find_board(info, b, filename): - break - if "with" in b: - b = b.split("with")[0].strip() - if find_board(info, b, filename): - break - info["board"] = "GENERIC" - info["board"] = info["board"].replace(" ", "_") - gc.collect() + info["board"] = get_boardname() try: - # extract build from uname().version if available - info["build"] = _build(os.uname()[3]) - if not info["build"]: - # extract build from uname().release if available - info["build"] = _build(os.uname()[2]) - if not info["build"] and ";" in sys.version: - # extract build from uname().release if available - info["build"] = _build(sys.version.split(";")[1]) - except (AttributeError, IndexError): + if "uname" in dir(os): # old + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) # type: ignore + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) # type: ignore + elif "version" in dir(sys): # new + # extract build from sys.version if available + info["build"] = _build(sys.version) + except (AttributeError, IndexError, TypeError): pass # avoid build hashes - if info["build"] and len(info["build"]) > 5: - info["build"] = "" + # if info["build"] and len(info["build"]) > 5: + # info["build"] = "" if info["version"] == "" and sys.platform not in ("unix", "win32"): try: - u = os.uname() + u = os.uname() # type: ignore info["version"] = u.release except (IndexError, AttributeError, TypeError): pass @@ -525,13 +585,14 @@ def _info(): # type:() -> dict[str, str] info["release"] = "2.0.0" if info["family"] == "micropython": + info["version"] if ( info["version"] and info["version"].endswith(".0") and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 and info["version"] <= "1.19.9" ): - # drop the .0 for newer releases + # versions from 1.10.0 to 1.20.0 do not have a micro .0 info["version"] = info["version"][:-2] # spell-checker: disable @@ -555,25 +616,31 @@ def _info(): # type:() -> dict[str, str] info["arch"] = arch # .mpy version.minor info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + if info["build"] and not info["version"].endswith("-preview"): + info["version"] = info["version"] + "-preview" # simple to use version[-build] string - info["ver"] = f"v{info['version']}-{info['build']}" if info["build"] else f"v{info['version']}" + info["ver"] = f"{info['version']}-{info['build']}" if info["build"] else f"{info['version']}" return info -def find_board(info: dict, board_descr: str, filename: str): - "Find the board in the provided board_info.csv file" - with open(filename, "r") as file: - # ugly code to make testable in python and micropython - while 1: - line = file.readline() - if not line: - break - descr_, board_ = line.split(",")[0].strip(), line.split(",")[1].strip() - if descr_ == board_descr: - info["board"] = board_ - return True - return False +def version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def get_boardname() -> str: + "Read the board name from the boardname.py file that may have been created upfront" + try: + from boardname import BOARDNAME # type: ignore + + log.info("Found BOARDNAME: {}".format(BOARDNAME)) + except ImportError: + log.warning("BOARDNAME not found") + BOARDNAME = "" + return BOARDNAME def get_root() -> str: # sourcery skip: use-assigned-variable @@ -626,10 +693,6 @@ def is_micropython() -> bool: # pylint: disable=unused-variable,eval-used try: # either test should fail on micropython - # a) https://docs.micropython.org/en/latest/genrst/syntax.html#spaces - # Micropython : SyntaxError - # a = eval("1and 0") # lgtm [py/unused-local-variable] - # Eval blocks some minification aspects # b) https://docs.micropython.org/en/latest/genrst/builtin_types.html#bytes-with-keywords-not-implemented # Micropython: NotImplementedError @@ -651,40 +714,40 @@ def main(): stubber.clean() # Read stubs from modulelist in the current folder or in /libs # fall back to default modules - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - _1 = gc.mem_free() # type: ignore - with open(p + "/modulelist.txt") as f: - print("Debug: List of modules: " + p + "/modulelist.txt") - line = f.readline() - while line: - line = line.strip() + def get_modulelist(stubber): + # new + gc.collect() + stubber.modules = [] # avoid duplicates + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + with open(fname) as f: + # print("DEBUG: list of modules: " + p + "/modulelist.txt") + while True: + line = f.readline().strip() + if not line: + break if len(line) > 0 and line[0] != "#": stubber.modules.append(line) - line = f.readline() - gc.collect() # type: ignore - print("Debug: Used memory to load modulelist.txt: " + str(_1 - gc.mem_free()) + " bytes") # type: ignore + gc.collect() + print("BREAK") break - except Exception: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - print("Could not find modulelist.txt, using default modules") + + if not stubber.modules: + stubber.modules = ["micropython"] + # _log.warn("Could not find modulelist.txt, using default modules") + gc.collect() + + stubber.modules = [] # avoid duplicates + get_modulelist(stubber) gc.collect() stubber.create_all_stubs() - stubber.report() if __name__ == "__main__" or is_micropython(): - try: - log = logging.getLogger("stubber") - logging.basicConfig(level=logging.INFO) - # logging.basicConfig(level=logging.DEBUG) - except NameError: - pass if not file_exists("no_auto_stubber.txt"): try: gc.threshold(4 * 1024) # type: ignore diff --git a/src/stubber/board/createstubs_mem_min.py b/src/stubber/board/createstubs_mem_min.py index 53bc8a3ae..9c2cfd272 100644 --- a/src/stubber/board/createstubs_mem_min.py +++ b/src/stubber/board/createstubs_mem_min.py @@ -1,284 +1,300 @@ -u='stubber' -t='{}/{}' -s='method' -r='function' -q='bool' -p='str' -o='float' -n='int' -m=NameError -l=sorted -k=NotImplementedError -b=',\n' -a='dict' -Z='list' -Y='tuple' -X='micropython' -W=str -V=repr -T='_' -S=KeyError -R=open -Q=IndexError -P=dir -O=ImportError -N=True -M='family' -L=len -K=print -J='board' -I='.' -H=AttributeError -A=False +x='No report file' +w='Failed to create the report.' +v='{}/{}' +u='method' +t='function' +s='bool' +r='str' +q='float' +p='int' +o='stubber' +n=TypeError +m=Exception +l=KeyError +k=sorted +j=NotImplementedError +e=',\n' +d='dict' +c='list' +b='tuple' +a='micropython' +Z=repr +W='-preview' +V='-' +U='board' +T=IndexError +S=print +R=True +Q='family' +P=len +O=open +N=ImportError +M=dir +K='port' +J='.' +I=AttributeError +H=False G='/' -E=None -D='version' F=OSError -C='' -import gc as B,os,sys -from ujson import dumps as c -try:from machine import reset -except O:pass -try:from collections import OrderedDict as d -except O:from ucollections import OrderedDict as d -__version__='v1.16.2' -v=2 -w=2 -e=[I,'/lib','/sd/lib','/flash/lib','lib'] +E=None +C='version' +B='' +import gc as D,os,sys from time import sleep +try:from ujson import dumps +except:from json import dumps +try:from machine import reset +except N:pass +try:from collections import OrderedDict as f +except N:from ucollections import OrderedDict as f +__version__='v1.17.0' +y=2 +z=2 +A0=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=S + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(o) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(A,path=E,firmware_id=E): + def __init__(B,path=E,firmware_id=E): C=firmware_id try: - if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise k('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();B.collect() - if C:A._fwid=C.lower() - elif A.info[M]==X:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=B.mem_free() + if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed') + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[U]));D.collect() + if C:B._fwid=C.lower() + elif B.info[Q]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(V) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=D.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:f(path+G) - except F:K('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:X(path+G) + except F:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in P(I): - if A.startswith(T)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in M(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=V(type(E)).split("'")[1] - except Q:F=C - if F in{n,o,p,q,Y,Z,a}:G=1 - elif F in{r,s}:G=2 + E=getattr(H,A) + try:F=Z(type(E)).split("'")[1] + except T:F=B + if F in{p,q,r,s,b,c,d}:G=1 + elif F in{t,u}:G=2 elif F in'class':G=3 else:G=4 - D.append((A,V(E),V(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=l([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);B.collect();return D,J - def add_modules(A,modules):A.modules=l(set(A.modules)|set(modules)) - def create_all_stubs(A): - B.collect() - for C in A.modules:A.create_one_stub(C) + C.append((A,Z(E),Z(type(E)),E,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except MemoryError as J:S('MemoryError: {}'.format(J));sleep(1);reset() + C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);D.collect();return C,K + def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules)) + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();D.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') def create_one_stub(C,module_name): - D=module_name - if D in C.problematic:return A - if D in C.excluded:return A - H='{}/{}.py'.format(C.path,D.replace(I,G));B.collect();E=A - try:E=C.create_module_stub(D,H) - except F:return A - B.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:M=D.replace(I,T)+'.py';H=J.path+G+M - else:M=H.split(G)[-1] - if G in D:D=D.replace(G,I) - K=E - try:K=__import__(D,E,E,'*');U=B.mem_free() - except O:return A - f(H) - with R(H,'w')as L:P=W(J.info).replace('OrderedDict(',C).replace('})','}');Q='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);L.write(Q);L.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(L,K,D,C) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os','sys','logging','gc'}: - try:del K - except(F,S):pass - try:del sys.modules[D] - except S:pass - B.collect();return N - def write_object_stub(M,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';Q=in_class;P=object_expr;O='Exception';H=fp;D=indent;B.collect() - if P in M.problematic:return - R,N=M.get_obj_attributes(P) - if N:K(N) - for(E,J,G,T,f)in R: - if E in['classmethod','staticmethod','BaseException',O]:continue - if E[0].isdigit():continue - if G==""and L(D)<=w*4: - U=C;V=E.endswith(O)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=O - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);M.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',Q+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[s,r,'closure']): - W=b;X=C - if Q>0:X='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,W) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,X,W) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith("5}'.format(C,L,Q)) + except N:return H + X(I) + with O(I,'w')as P:S=str(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,M,C,B) + K.report_add(C,I) + if C not in{'os','sys','logging','gc'}: + try:del M + except(F,l):A.warning('could not del new_module') + D.collect();return R + def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): + X='generator';W='{0}{1}: {3} = {2}\n';V='bound_method';U='Incomplete';N=in_class;M='Exception';L=object_expr;I=fp;E=indent;D.collect() + if L in K.problematic:A.warning('SKIPPING problematic module:{}'.format(L));return + Y,O=K.get_obj_attributes(L) + if O:A.error(O) + for(F,J,H,Z,e)in Y: + if F in['classmethod','staticmethod','BaseException',M]:continue + if F[0].isdigit():A.warning('NameError: invalid name {}'.format(F));continue + if H==""and P(E)<=z*4: + Q=B;R=F.endswith(M)or F.endswith('Error')or F in['KeyboardInterrupt','StopIteration','SystemExit'] + if R:Q=M + C='\n{}class {}({}):\n'.format(E,F,Q) + if R:C+=E+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,F),E+' ',N+1);C=E+' def __init__(self, *argv, **kwargs) -> None:\n';C+=E+' ...\n\n';I.write(C) + elif any(A in H for A in[u,t,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(E)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(E,F,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(E,F,T,S) + C+=E+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=C - if A[D]==C and sys.platform not in(k,j): - try:o=os.uname();A[D]=o.release - except(Q,H,TypeError):pass - for(p,q,r)in[(l,l,'const'),(m,m,'FAT'),(n,'pybricks.hubs','EV3Brick')]: - try:s=__import__(q,E,E,r);A[M]=p;del s;break - except(O,S):pass - if A[M]==n:A['release']='2.0.0' - if A[M]==X: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if K in A and A[K]: - V=int(A[K]);a=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][V>>10] - if a:A[f]=a - A[K]='v{}.{}'.format(V&255,V>>8&3) - A[b]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def g(info,board_descr,filename): - with R(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return N - return A + if'uname'in M(os): + A[D]=Y(os.uname()[3]) + if not A[D]:A[D]=Y(os.uname()[2]) + elif C in M(sys):A[D]=Y(sys.version) + except(I,T,n):pass + if A[C]==B and sys.platform not in(S,R): + try:b=os.uname();A[C]=b.release + except(T,I,n):pass + for(c,d,e)in[(V,V,'const'),(X,X,'FAT'),(Z,'pybricks.hubs','EV3Brick')]: + try:g=__import__(d,E,E,e);A[Q]=c;del g;break + except(N,l):pass + if A[Q]==Z:A['release']='2.0.0' + if A[Q]==a: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[P]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[D]and not A[C].endswith(W):A[C]=A[C]+W + A[L]=f"{A[C]}-{A[D]}"if A[D]else f"{A[C]}";return A +def A1(version): + A=version;B=J.join([str(A)for A in A[:3]]) + if P(A)>3 and A[3]:B+=V+A[3] + return B +def A2(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except N:A.warning('BOARDNAME not found');C=B + return C def get_root(): try:A=os.getcwd() - except(F,H):A=I + except(F,I):A=J B=A - for B in[A,'/sd','/flash',G,I]: + for B in[A,'/sd','/flash',G,J]: try:C=os.stat(B);break except F:continue return B -def h(filename): +def g(filename): try: - if os.stat(filename)[0]>>14:return N - return A - except F:return A -def i():sys.exit(1) + if os.stat(filename)[0]>>14:return R + return H + except F:return H +def h():S("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): - path=C - if L(sys.argv)==3: + path=B + if P(sys.argv)==3: A=sys.argv[1].lower() if A in('--path','-p'):path=sys.argv[2] - else:i() - elif L(sys.argv)==2:i() + else:h() + elif P(sys.argv)==2:h() return path -def j(): - try:B=bytes('abc',encoding='utf8');C=j.__module__;return A - except(k,H):return N +def i(): + try:A=bytes('abc',encoding='utf8');B=i.__module__;return H + except(j,I):return R def main(): - E='/modulelist.txt';stubber=Stubber(path=read_path());stubber.clean();stubber.modules=[] - for C in e: - try: - F=B.mem_free() - with R(C+E)as D: - K('Debug: List of modules: '+C+E);A=D.readline() - while A: - A=A.strip() - if L(A)>0 and A[0]!='#':stubber.modules.append(A) - A=D.readline() - B.collect();K('Debug: Used memory to load modulelist.txt: '+W(F-B.mem_free())+' bytes');break - except Exception:pass - if not stubber.modules:stubber.modules=[X] - B.collect();stubber.create_all_stubs();stubber.report() -if __name__=='__main__'or j(): - try:x=logging.getLogger(u);logging.basicConfig(level=logging.INFO) - except m:pass - if not h('no_auto_stubber.txt'): - try:B.threshold(4*1024);B.enable() + stubber=Stubber(path=read_path());stubber.clean() + def A(stubber): + D.collect();stubber.modules=[] + for C in A0: + B=C+'/modulelist.txt' + if not g(B):continue + with O(B)as E: + while R: + A=E.readline().strip() + if not A:break + if P(A)>0 and A[0]!='#':stubber.modules.append(A) + D.collect();S('BREAK');break + if not stubber.modules:stubber.modules=[a] + D.collect() + stubber.modules=[];A(stubber);D.collect();stubber.create_all_stubs() +if __name__=='__main__'or i(): + if not g('no_auto_stubber.txt'): + try:D.threshold(4*1024);D.enable() except BaseException:pass main() \ No newline at end of file diff --git a/src/stubber/board/createstubs_mem_mpy.mpy b/src/stubber/board/createstubs_mem_mpy.mpy index 6edd7f8c5..05ce24a08 100644 Binary files a/src/stubber/board/createstubs_mem_mpy.mpy and b/src/stubber/board/createstubs_mem_mpy.mpy differ diff --git a/src/stubber/board/createstubs_min.py b/src/stubber/board/createstubs_min.py index a74db7725..63ab05685 100644 --- a/src/stubber/board/createstubs_min.py +++ b/src/stubber/board/createstubs_min.py @@ -1,257 +1,274 @@ -w='pyb' -v='stubber' -u='{}/{}' -t='logging' -s='sys' -r='method' -q='function' -p='bool' -o='str' -n='float' -m='int' -l=NameError +z='No report file' +y='Failed to create the report.' +x='{}/{}' +w='logging' +v='sys' +u='method' +t='function' +s='bool' +r='str' +q='float' +p='int' +o='stubber' +n=TypeError +m=Exception +l=KeyError k=sorted j=NotImplementedError -b='pycom' -a=',\n' -Z='dict' -Y='list' -X='tuple' -W='micropython' -V=open -U=repr -S='_' +f='pycom' +e=',\n' +d='dict' +c='list' +b='tuple' +a='micropython' +Z=repr +Y=print +V='-preview' +U=True +T='-' +S='board' R=len -Q=KeyError +Q=open P=IndexError -O=dir -M=print +O='family' N=ImportError -K=True -L='family' -J='board' -I='.' -H=AttributeError -A=False +M=dir +K='port' +J='.' +I=AttributeError +H=False G='/' -E=None -F=OSError -D='version' +E=OSError +D=None +C='version' B='' -import gc as C,os,sys -from ujson import dumps as c +import gc as F,os,sys +from time import sleep +try:from ujson import dumps +except:from json import dumps try:from machine import reset except N:pass -try:from collections import OrderedDict as d -except N:from ucollections import OrderedDict as d -__version__='v1.16.2' -x=2 -y=2 -z=[I,'/lib','/sd/lib','/flash/lib','lib'] -from time import sleep +try:from collections import OrderedDict as g +except N:from ucollections import OrderedDict as g +__version__='v1.17.0' +A0=2 +A1=2 +A5=['lib','/lib','/sd/lib','/flash/lib',J] +class L: + INFO=20;WARNING=30;ERROR=40;level=INFO;prnt=Y + @staticmethod + def getLogger(name):return L() + @classmethod + def basicConfig(A,level):A.level=level + def info(A,msg): + if A.level<=L.INFO:A.prnt('INFO :',msg) + def warning(A,msg): + if A.level<=L.WARNING:A.prnt('WARN :',msg) + def error(A,msg): + if A.level<=L.ERROR:A.prnt('ERROR :',msg) +A=L.getLogger(o) +L.basicConfig(level=L.INFO) class Stubber: - def __init__(A,path=E,firmware_id=E): - B=firmware_id + def __init__(B,path=D,firmware_id=D): + C=firmware_id try: if os.uname().release=='1.13.0'and os.uname().version<'v1.13-103':raise j('MicroPython 1.13.0 cannot be stubbed') - except H:pass - A._report=[];A.info=_info();C.collect() - if B:A._fwid=B.lower() - elif A.info[L]==W:A._fwid='{family}-{ver}-{port}-{board}'.format(**A.info) - else:A._fwid='{family}-{ver}-{port}'.format(**A.info) - A._start_free=C.mem_free() + except I:pass + B.info=_info();A.info('Port: {}'.format(B.info[K]));A.info('Board: {}'.format(B.info[S]));F.collect() + if C:B._fwid=C.lower() + elif B.info[O]==a:B._fwid='{family}-v{version}-{port}-{board}'.format(**B.info).rstrip(T) + else:B._fwid='{family}-v{version}-{port}'.format(**B.info) + B._start_free=F.mem_free() if path: if path.endswith(G):path=path[:-1] else:path=get_root() - A.path='{}/stubs/{}'.format(path,A.flat_fwid).replace('//',G) - try:e(path+G) - except F:M('error creating stub folder {}'.format(path)) - A.problematic=['upip','upysh','webrepl_setup','http_client','http_client_ssl','http_server','http_server_ssl'];A.excluded=['webrepl','_webrepl','port_diag','example_sub_led.py','example_pub_button.py'];A.modules=[] + B.path='{}/stubs/{}'.format(path,B.flat_fwid).replace('//',G) + try:W(path+G) + except E:A.error('error creating stub folder {}'.format(path)) + 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 def get_obj_attributes(L,item_instance): - I=item_instance;D=[];J=[] - for A in O(I): - if A.startswith(S)and not A in L.modules:continue + H=item_instance;C=[];K=[] + for A in M(H): + if A.startswith('__')and not A in L.modules:continue try: - E=getattr(I,A) - try:F=U(type(E)).split("'")[1] - except P:F=B - if F in{m,n,o,p,X,Y,Z}:G=1 - elif F in{q,r}:G=2 - elif F in'class':G=3 + D=getattr(H,A) + try:E=Z(type(D)).split("'")[1] + except P:E=B + if E in{p,q,r,s,b,c,d}:G=1 + elif E in{t,u}:G=2 + elif E in'class':G=3 else:G=4 - D.append((A,U(E),U(type(E)),E,G)) - except H as K:J.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,I,K)) - except MemoryError as K:sleep(1);reset() - D=k([A for A in D if not A[0].startswith('__')],key=lambda x:x[4]);C.collect();return D,J + C.append((A,Z(D),Z(type(D)),D,G)) + except I as J:K.append("Couldn't get attribute '{}' from object '{}', Err: {}".format(A,H,J)) + except MemoryError as J:Y('MemoryError: {}'.format(J));sleep(1);reset() + C=k([A for A in C if not A[0].startswith('__')],key=lambda x:x[4]);F.collect();return C,K def add_modules(A,modules):A.modules=k(set(A.modules)|set(modules)) - def create_all_stubs(A): - C.collect() - for B in A.modules:A.create_one_stub(B) - def create_one_stub(B,module_name): - D=module_name - if D in B.problematic:return A - if D in B.excluded:return A - H='{}/{}.py'.format(B.path,D.replace(I,G));C.collect();E=A - try:E=B.create_module_stub(D,H) - except F:return A - C.collect();return E - def create_module_stub(J,module_name,file_name=E): - H=file_name;D=module_name - if H is E:O=D.replace(I,S)+'.py';H=J.path+G+O - else:O=H.split(G)[-1] - if G in D:D=D.replace(G,I) - L=E - try:L=__import__(D,E,E,'*');T=C.mem_free() - except N:return A - e(H) - with V(H,'w')as M:P=str(J.info).replace('OrderedDict(',B).replace('})','}');R='"""\nModule: \'{0}\' on {1}\n"""\n# MCU: {2}\n# Stubber: {3}\n'.format(D,J._fwid,P,__version__);M.write(R);M.write('from typing import Any\nfrom _typeshed import Incomplete\n\n');J.write_object_stub(M,L,D,B) - J._report.append('{{"module": "{}", "file": "{}"}}'.format(D,H.replace('\\',G))) - if D not in{'os',s,t,'gc'}: - try:del L - except(F,Q):pass - try:del sys.modules[D] - except Q:pass - C.collect();return K + def create_all_stubs(B): + A.info('Start micropython-stubber {} on {}'.format(__version__,B._fwid));B.report_start();F.collect() + for C in B.modules:B.create_one_stub(C) + B.report_end();A.info('Finally done') + def create_one_stub(C,module_name): + B=module_name + if B in C.problematic:A.warning('Skip module: {:<25} : Known problematic'.format(B));return H + if B in C.excluded:A.warning('Skip module: {:<25} : Excluded'.format(B));return H + I='{}/{}.pyi'.format(C.path,B.replace(J,G));F.collect();D=H + try:D=C.create_module_stub(B,I) + except E:return H + F.collect();return D + def create_module_stub(K,module_name,file_name=D): + I=file_name;C=module_name + if I is D:L=C.replace(J,'_')+'.pyi';I=K.path+G+L + else:L=I.split(G)[-1] + if G in C:C=C.replace(G,J) + M=D + try:M=__import__(C,D,D,'*');P=F.mem_free();A.info('Stub module: {:<25} to file: {:<70} mem:{:>5}'.format(C,L,P)) + except N:return H + W(I) + 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) + K.report_add(C,I) + if C not in{'os',v,w,'gc'}: + try:del M + except(E,l):A.warning('could not del new_module') + F.collect();return U def write_object_stub(K,fp,object_expr,obj_name,indent,in_class=0): - d='{0}{1} = {2} # type: {3}\n';c='bound_method';b='Incomplete';P=in_class;O=object_expr;N='Exception';H=fp;D=indent;C.collect() - if O in K.problematic:return - S,L=K.get_obj_attributes(O) - if L:M(L) - for(E,J,G,T,f)in S: - if E in['classmethod','staticmethod','BaseException',N]:continue - if E[0].isdigit():continue - if G==""and R(D)<=y*4: - U=B;V=E.endswith(N)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] - if V:U=N - A='\n{}class {}({}):\n'.format(D,E,U) - if V:A+=D+' ...\n';H.write(A);return - H.write(A);K.write_object_stub(H,T,'{0}.{1}'.format(obj_name,E),D+' ',P+1);A=D+' def __init__(self, *argv, **kwargs) -> None:\n';A+=D+' ...\n\n';H.write(A) - elif any(A in G for A in[r,q,'closure']): - W=b;a=B - if P>0:a='self, ' - if c in G or c in J:A='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,W) - else:A='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,a,W) - A+=D+' ...\n\n';H.write(A) - elif G=="":0 - elif G.startswith(""and R(D)<=A1*4: + P=B;Q=E.endswith(M)or E.endswith('Error')or E in['KeyboardInterrupt','StopIteration','SystemExit'] + if Q:P=M + C='\n{}class {}({}):\n'.format(D,E,P) + if Q:C+=D+' ...\n';I.write(C);continue + I.write(C);K.write_object_stub(I,Z,'{0}.{1}'.format(obj_name,E),D+' ',N+1);C=D+' def __init__(self, *argv, **kwargs) -> None:\n';C+=D+' ...\n\n';I.write(C) + elif any(A in H for A in[u,t,'closure']): + S=U;T=B + if N>0:T='self, ' + if V in H or V in J:C='{}@classmethod\n'.format(D)+'{}def {}(cls, *args, **kwargs) -> {}:\n'.format(D,E,S) + else:C='{}def {}({}*args, **kwargs) -> {}:\n'.format(D,E,T,S) + C+=D+' ...\n\n';I.write(C) + elif H=="":0 + elif H.startswith("5:A[F]=B - if A[D]==B and sys.platform not in(j,i): - try:m=os.uname();A[D]=m.release - except(P,H,TypeError):pass - for(n,o,p)in[(k,k,'const'),(b,b,'FAT'),(l,'pybricks.hubs','EV3Brick')]: - try:q=__import__(o,E,E,p);A[L]=n;del q;break - except(N,Q):pass - if A[L]==l:A['release']='2.0.0' - if A[L]==W: - if A[D]and A[D].endswith('.0')and A[D]>='1.10.0'and A[D]<='1.19.9':A[D]=A[D][:-2] - if K in A and A[K]: - V=int(A[K]);Z=[E,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][V>>10] - if Z:A[e]=Z - A[K]='v{}.{}'.format(V&255,V>>8&3) - A[a]=f"v{A[D]}-{A[F]}"if A[F]else f"v{A[D]}";return A -def f(info,board_descr,filename): - with V(filename,'r')as C: - while 1: - B=C.readline() - if not B:break - D,E=B.split(',')[0].strip(),B.split(',')[1].strip() - if D==board_descr:info[J]=E;return K - return A + if'uname'in M(os): + A[E]=X(os.uname()[3]) + if not A[E]:A[E]=X(os.uname()[2]) + elif C in M(sys):A[E]=X(sys.version) + except(I,P,n):pass + if A[C]==B and sys.platform not in(U,T): + try:Z=os.uname();A[C]=Z.release + except(P,I,n):pass + for(b,c,d)in[(W,W,'const'),(f,f,'FAT'),(Y,'pybricks.hubs','EV3Brick')]: + try:e=__import__(c,D,D,d);A[O]=b;del e;break + except(N,l):pass + if A[O]==Y:A['release']='2.0.0' + if A[O]==a: + A[C] + 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] + if F in A and A[F]: + G=int(A[F]);J=[D,'x86','x64','armv6','armv6m','armv7m','armv7em','armv7emsp','armv7emdp','xtensa','xtensawin'][G>>10] + if J:A[R]=J + A[F]='v{}.{}'.format(G&255,G>>8&3) + if A[E]and not A[C].endswith(V):A[C]=A[C]+V + A[L]=f"{A[C]}-{A[E]}"if A[E]else f"{A[C]}";return A +def A2(version): + A=version;B=J.join([str(A)for A in A[:3]]) + if R(A)>3 and A[3]:B+=T+A[3] + return B +def A3(): + try:from boardname import BOARDNAME as C;A.info('Found BOARDNAME: {}'.format(C)) + except N:A.warning('BOARDNAME not found');C=B + return C def get_root(): try:A=os.getcwd() - except(F,H):A=I + except(E,I):A=J B=A - for B in[A,'/sd','/flash',G,I]: + for B in[A,'/sd','/flash',G,J]: try:C=os.stat(B);break - except F:continue + except E:continue return B -def g(filename): +def A4(filename): try: - if os.stat(filename)[0]>>14:return K - return A - except F:return A -def h():sys.exit(1) + if os.stat(filename)[0]>>14:return U + return H + except E:return H +def h():Y("-p, --path path to store the stubs in, defaults to '.'");sys.exit(1) def read_path(): path=B if R(sys.argv)==3: @@ -261,13 +278,11 @@ def read_path(): elif R(sys.argv)==2:h() return path def i(): - try:B=bytes('abc',encoding='utf8');C=i.__module__;return A - except(j,H):return K -def main():stubber=Stubber(path=read_path());stubber.clean();stubber.modules=['WM8960','_OTA','_asyncio','_boot_fat','_coap','_espnow','_flash_control_OTA','_main_pybytes','_mqtt','_mqtt_core','_msg_handl','_onewire','_periodical_pin','_pybytes','_pybytes_ca','_pybytes_config','_pybytes_config_reader','_pybytes_connection','_pybytes_constants','_pybytes_debug','_pybytes_library','_pybytes_machine_learning','_pybytes_main','_pybytes_protocol','_pybytes_pyconfig','_pybytes_pymesh_config','_rp2','_terminal','_thread','_uasyncio','_urequest','adcfft','aioble/__init__','aioble/central','aioble/client','aioble/core','aioble/device','aioble/l2cap','aioble/peripheral','aioble/security','aioble/server','aioespnow','ak8963','apa102','apa106','argparse','array','asyncio/__init__','asyncio/core','asyncio/event','asyncio/funcs','asyncio/lock','asyncio/stream','binascii','bluetooth','breakout_as7262','breakout_bh1745','breakout_bme280','breakout_bme68x','breakout_bmp280','breakout_dotmatrix','breakout_encoder','breakout_icp10125','breakout_ioexpander','breakout_ltr559','breakout_matrix11x7','breakout_mics6814','breakout_msa301','breakout_paa5100','breakout_pmw3901','breakout_potentiometer','breakout_rgbmatrix5x5','breakout_rtc','breakout_scd41','breakout_sgp30','breakout_trackball','breakout_vl53l5cx','btree','cmath','collections','crypto','cryptolib','curl','deflate','dht','display','display_driver_utils','ds18x20','encoder','errno','esp','esp32','espidf','espnow','ffi','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',t,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',W,'mip','mip/__init__','mip/__main__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform',w,b,'pye','qrcode','queue','random','requests','requests/__init__','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',s,'termios','time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',D,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];C.collect();stubber.create_all_stubs();stubber.report() + try:A=bytes('abc',encoding='utf8');B=i.__module__;return H + except(j,I):return U +def main():stubber=Stubber(path=read_path());stubber.clean();stubber.modules=['WM8960','_OTA','_asyncio','_boot_fat','_coap','_espnow','_flash_control_OTA','_main_pybytes','_mqtt','_mqtt_core','_msg_handl','_onewire','_periodical_pin','_pybytes','_pybytes_ca','_pybytes_config','_pybytes_config_reader','_pybytes_connection','_pybytes_constants','_pybytes_debug','_pybytes_library','_pybytes_machine_learning','_pybytes_main','_pybytes_protocol','_pybytes_pyconfig','_pybytes_pymesh_config','_rp2','_terminal','_thread','_uasyncio','_urequest','adcfft','aioble/__init__','aioble/central','aioble/client','aioble/core','aioble/device','aioble/l2cap','aioble/peripheral','aioble/security','aioble/server','aioespnow','ak8963','apa102','apa106','argparse','array','asyncio/__init__','asyncio/core','asyncio/event','asyncio/funcs','asyncio/lock','asyncio/stream','binascii','bluetooth','breakout_as7262','breakout_bh1745','breakout_bme280','breakout_bme68x','breakout_bmp280','breakout_dotmatrix','breakout_encoder','breakout_icp10125','breakout_ioexpander','breakout_ltr559','breakout_matrix11x7','breakout_mics6814','breakout_msa301','breakout_paa5100','breakout_pmw3901','breakout_potentiometer','breakout_rgbmatrix5x5','breakout_rtc','breakout_scd41','breakout_sgp30','breakout_trackball','breakout_vl53l5cx','btree','cmath','collections','crypto','cryptolib','curl','deflate','dht','display','display_driver_utils','ds18x20','encoder','errno','esp','esp32','espidf','espnow','ffi','flashbdev','framebuf','freesans20','fs_driver','functools','galactic','gc','gfx_pack','gsm','hashlib','heapq','hub75','ili9341','ili9XXX','imagetools','inisetup','interstate75','io','jpegdec','json','lcd160cr','lodepng',w,'lsm6dsox','lv_colors','lv_utils','lvgl','lwip','machine','math','microWebSocket','microWebSrv','microWebTemplate',a,'mip','mip/__init__','mip/__main__','motor','mpu6500','mpu9250','neopixel','network','ntptime','onewire','os','pcf85063a','picoexplorer','picographics','picokeypad','picoscroll','picounicorn','picowireless','pimoroni','pimoroni_bus','pimoroni_i2c','plasma','platform','pyb',f,'pye','qrcode','queue','random','requests','requests/__init__','rp2','rtch','samd','select','servo','socket','ssd1306','ssh','ssl','stm','struct',v,'termios','time','tpcalib','uarray','uasyncio/__init__','uasyncio/core','uasyncio/event','uasyncio/funcs','uasyncio/lock','uasyncio/stream','uasyncio/tasks','ubinascii','ubluetooth','ucollections','ucrypto','ucryptolib','uctypes','uerrno','uftpd','uhashlib','uheapq','uio','ujson','ulab','ulab/approx','ulab/compare','ulab/fft','ulab/filter','ulab/linalg','ulab/numerical','ulab/poly','ulab/user','ulab/vector','umachine','umqtt/__init__','umqtt/robust','umqtt/simple','uos','uplatform','uqueue','urandom','ure','urequests','urllib/urequest','uselect','usocket','ussl','ustruct','usys','utelnetserver','utime','utimeq','uwebsocket','uzlib',C,'websocket','websocket_helper','wipy','writer','xpt2046','ymodem','zephyr','zlib'];F.collect();stubber.create_all_stubs() if __name__=='__main__'or i(): - try:A0=logging.getLogger(v);logging.basicConfig(level=logging.INFO) - except l:pass - if not g('no_auto_stubber.txt'): - try:C.threshold(4*1024);C.enable() + if not A4('no_auto_stubber.txt'): + try:F.threshold(4*1024);F.enable() except BaseException:pass main() \ No newline at end of file diff --git a/src/stubber/board/createstubs_mpy.mpy b/src/stubber/board/createstubs_mpy.mpy index 80672bb8c..49ccf4652 100644 Binary files a/src/stubber/board/createstubs_mpy.mpy and b/src/stubber/board/createstubs_mpy.mpy differ diff --git a/src/stubber/board/fw_info.py b/src/stubber/board/fw_info.py new file mode 100644 index 000000000..1dc082953 --- /dev/null +++ b/src/stubber/board/fw_info.py @@ -0,0 +1,135 @@ +# %%micropython +import os +import sys + + +def _build(s): + # extract a build nr from a string + if not s: + return "" + if " on " in s: + s = s.split(" on ", 1)[0] + return s.split("-")[1] if "-" in s else "" + + +def _version_str(version: tuple): # -> str: + v_str = ".".join([str(n) for n in version[:3]]) + if len(version) > 3 and version[3]: + v_str += "-" + version[3] + return v_str + + +def _info(): # type:() -> dict[str, str] + # sourcery skip: use-contextlib-suppress, use-fstring-for-formatting, use-named-expression + info = dict( + { + "family": sys.implementation.name, + "version": "", + "build": "", + "ver": "", + "port": "stm32" if sys.platform.startswith("pyb") else sys.platform, # port: esp32 / win32 / linux / stm32 + "board": "GENERIC", + "cpu": "", + "mpy": "", + "arch": "", + } + ) + try: + info["version"] = _version_str(sys.implementation.version) + except AttributeError: + pass + try: + machine = sys.implementation._machine if "_machine" in dir(sys.implementation) else os.uname().machine + info["board"] = machine.strip() + info["cpu"] = machine.split("with")[-1].strip() if "with" in machine else "" + info["mpy"] = ( + sys.implementation._mpy + if "_mpy" in dir(sys.implementation) + else sys.implementation.mpy + if "mpy" in dir(sys.implementation) + else "" + ) + except (AttributeError, IndexError): + pass + + try: + # extract build from uname().version if available + info["build"] = _build(os.uname()[3]) + if not info["build"]: + # extract build from uname().release if available + info["build"] = _build(os.uname()[2]) + if not info["build"] and ";" in sys.version: + # extract build from uname().release if available + info["build"] = _build(sys.version.split(";")[1]) + except (AttributeError, IndexError): + pass + # avoid build hashes + if info["build"] and len(info["build"]) > 5: + info["build"] = "" + + if info["version"] == "" and sys.platform not in ("unix", "win32"): + try: + u = os.uname() + info["version"] = u.release + except (IndexError, AttributeError, TypeError): + pass + # detect families + for fam_name, mod_name, mod_thing in [ + ("pycopy", "pycopy", "const"), + ("pycom", "pycom", "FAT"), + ("ev3-pybricks", "pybricks.hubs", "EV3Brick"), + ]: + try: + _t = __import__(mod_name, None, None, (mod_thing)) + info["family"] = fam_name + del _t + break + except (ImportError, KeyError): + pass + + if info["family"] == "ev3-pybricks": + info["release"] = "2.0.0" + + if info["family"] == "micropython": + if ( + info["version"] + and info["version"].endswith(".0") + and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.20.0 do not have a micro .0 + and info["version"] <= "1.19.9" + ): + # drop the .0 for newer releases + info["version"] = info["version"][:-2] + + # spell-checker: disable + if "mpy" in info and info["mpy"]: # mpy on some v1.11+ builds + sys_mpy = int(info["mpy"]) + # .mpy architecture + arch = [ + None, + "x86", + "x64", + "armv6", + "armv6m", + "armv7m", + "armv7em", + "armv7emsp", + "armv7emdp", + "xtensa", + "xtensawin", + ][sys_mpy >> 10] + if arch: + info["arch"] = arch + # .mpy version.minor + info["mpy"] = "v{}.{}".format(sys_mpy & 0xFF, sys_mpy >> 8 & 3) + # simple to use version[-build] string avoiding f-strings for backward compat + info["ver"] = ( + "v{version}-{build}".format(version=info["version"], build=info["build"]) + if info["build"] + else "v{version}".format(version=info["version"]) + ) + + return info + + +print(_info()) +# del _info, _build, _version_str diff --git a/src/stubber/board/logging.py b/src/stubber/board/logging.py deleted file mode 100644 index c0657df2d..000000000 --- a/src/stubber/board/logging.py +++ /dev/null @@ -1,99 +0,0 @@ -import sys - -CRITICAL = 50 -ERROR = 40 -WARNING = 30 -INFO = 20 -DEBUG = 10 -NOTSET = 0 - -_level_dict = { - CRITICAL: "CRIT", - ERROR: "ERROR", - WARNING: "WARN", - INFO: "INFO", - DEBUG: "DEBUG", -} - -_stream = sys.stderr - - -class Logger: - - level = NOTSET - - def __init__(self, name): - self.name = name - - def _level_str(self, level): - l = _level_dict.get(level) - if l is not None: - return l - return "LVL%s" % level - - def setLevel(self, level): - self.level = level - - def isEnabledFor(self, level): - return level >= (self.level or _level) - - def log(self, level, msg, *args): - if level >= (self.level or _level): - _stream.write("%-6s:%-8s:" % (self._level_str(level), self.name)) - if not args: - print(msg, file=_stream) - else: - print(msg % args, file=_stream) - - def debug(self, msg, *args): - self.log(DEBUG, msg, *args) - - def info(self, msg, *args): - self.log(INFO, msg, *args) - - def warning(self, msg, *args): - self.log(WARNING, msg, *args) - - def error(self, msg, *args): - self.log(ERROR, msg, *args) - - def critical(self, msg, *args): - self.log(CRITICAL, msg, *args) - - # def exc(self, e, msg, *args): - # self.log(ERROR, msg, *args) - # sys.print_exception(e, _stream) # type : disable - - def exception(self, msg, *args): - self.critical(sys.exc_info()[1], msg, *args) - - -_level = INFO -_loggers = {} - - -def getLogger(name): - if name in _loggers: - return _loggers[name] - l = Logger(name) - _loggers[name] = l - return l - - -def info(msg, *args): - getLogger(None).info(msg, *args) - - -def debug(msg, *args): - getLogger(None).debug(msg, *args) - - -def basicConfig(level=INFO, filename=None, stream=None, format=None): - global _level, _stream - _level = level - if stream: - _stream = stream - if filename is not None: - print("logging.basicConfig: filename arg is not supported") - if format is not None: - print("logging.basicConfig: format arg is not supported") diff --git a/src/stubber/board/modulelist.txt b/src/stubber/board/modulelist.txt index d722c404c..32dacba0f 100644 --- a/src/stubber/board/modulelist.txt +++ b/src/stubber/board/modulelist.txt @@ -1,5 +1,4 @@ -# list of modules to stub. -# used by the memory optimised createstubs_mem.py +# list of modules to stub used by the memory optimised createstubs_mem.py WM8960 _OTA _asyncio diff --git a/src/stubber/codemod/_partials/__init__.py b/src/stubber/codemod/_partials/__init__.py index fed7cac06..1d57d9aef 100644 --- a/src/stubber/codemod/_partials/__init__.py +++ b/src/stubber/codemod/_partials/__init__.py @@ -15,7 +15,7 @@ def _read_partial(path: Path) -> Iterator[str]: Read a partial from the file at `path` and yield only the lines between the ###PARTIAL### and ###PARTIALEND### markers """ - lines = deque(path.read_text().splitlines(keepends=True)) + lines = deque(path.read_text(encoding="utf-8").splitlines(keepends=True)) _start = False _end = False # todo: allow processing of files that do not have the markers diff --git a/src/stubber/codemod/_partials/db_main.py b/src/stubber/codemod/_partials/db_main.py index 31b251402..4e38012ee 100644 --- a/src/stubber/codemod/_partials/db_main.py +++ b/src/stubber/codemod/_partials/db_main.py @@ -3,20 +3,30 @@ - type_check_only is used to avoid circular imports The partial is enclosed in ###PARTIAL### and ###PARTIALEND### markers """ +# sourcery skip: require-parameter-annotation, for-append-to-extend, use-named-expression from io import TextIOWrapper from typing import TYPE_CHECKING, List, type_check_only -# sourcery skip: require-parameter-annotation if TYPE_CHECKING: + import gc import logging import sys - @type_check_only + class logging: + def getLogger(self, name: str) -> "logging": + ... + + def info(self, msg: str) -> None: + ... + + log = logging() + class Stubber: path: str _report: List[str] modules = [] + _json_name: str def __init__(self, path: str = "", firmware_id: str = "") -> None: ... @@ -27,114 +37,122 @@ def clean(self) -> None: def create_one_stub(self, modulename: str) -> bool: ... - def report(self, filename: str = "modules.json"): + def report_start(self, filename: str = "modules.json"): ... - def write_json_header(self, f: TextIOWrapper): - ... - - def write_json_node(self, f: TextIOWrapper, n, first=False): - ... - - def write_json_end(self, f): + def report_end(self): ... def create_all_stubs(self): ... - @type_check_only def read_path() -> str: ... - @type_check_only class _gc: def collect(self) -> None: ... gc: _gc - _log = logging.getLogger("stubber") + log = logging.getLogger("stubber") + + def file_exists(filename: str) -> bool: + ... LIBS = [".", "lib"] ###PARTIAL### -def main(): - import machine # type: ignore +SKIP_FILE = "modulelist.done" + + +def get_modules(skip=0): + # new + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + try: + with open(fname) as f: + i = 0 + while True: + line = f.readline().strip() + if not line: + break + if len(line) > 0 and line[0] == "#": + continue + i += 1 + if i < skip: + continue + yield line + break + except OSError: + pass + +def write_skip(done): + # write count of modules already processed to file + with open(SKIP_FILE, "w") as f: + f.write(str(done) + "\n") + + +def read_skip(): + # read count of modules already processed from file + done = 0 try: - f = open("modulelist.done", "r+b") - was_running = True - print("Opened existing db") + with open(SKIP_FILE) as f: + done = int(f.readline().strip()) except OSError: - f = open("modulelist.done", "w+b") - print("created new db") - was_running = False + pass + return done + + +def main(): + import machine # type: ignore + + was_running = file_exists(SKIP_FILE) + if was_running: + log.info("Continue from last run") + else: + log.info("Starting new run") + # try: + # f = open("modulelist.done", "r+b") + # was_running = True + # print("Continue from last run") + # except OSError: + # f = open("modulelist.done", "w+b") + # was_running = False stubber = Stubber(path=read_path()) # f_name = "{}/{}".format(stubber.path, "modules.json") + skip = 0 if not was_running: # Only clean folder if this is a first run stubber.clean() - # get list of modules to process - get_modulelist(stubber) - # remove the ones that are already done - modules_done = {} # type: dict[str, str] - try: - with open("modulelist.done") as f: - # not optimal , but works on mpremote and esp8266 - for line in f.read().split("\n"): - line = line.strip() - gc.collect() - if len(line) > 0: - key, value = line.split("=", 1) - modules_done[key] = value - except (OSError, SyntaxError): - pass - gc.collect() - # see if we can continue from where we left off - modules = [m for m in stubber.modules if m not in modules_done.keys()] - gc.collect() - for modulename in modules: + stubber.report_start("modules.json") + else: + skip = read_skip() + stubber._json_name = "{}/{}".format(stubber.path, "modules.json") + + for modulename in get_modules(skip): # ------------------------------------ # do epic shit # but sometimes things fail / run out of memory and reboot - ok = False try: - ok = stubber.create_one_stub(modulename) + stubber.create_one_stub(modulename) except MemoryError: # RESET AND HOPE THAT IN THE NEXT CYCLE WE PROGRESS FURTHER machine.reset() # ------------------------------------- gc.collect() - modules_done[modulename] = str(stubber._report[-1] if ok else "failed") - with open("modulelist.done", "a") as f: - f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) - - # Finished processing - load all the results , and remove the failed ones - if modules_done: - # stubber.write_json_end(mod_fp) - stubber._report = [v for _, v in modules_done.items() if v != "failed"] - stubber.report() - - -def get_modulelist(stubber): - stubber.modules = [] # avoid duplicates - for p in LIBS: - try: - with open(p + "/modulelist.txt") as f: - print("DEBUG: list of modules: " + p + "/modulelist.txt") - for line in f.read().split("\n"): - line = line.strip() - if len(line) > 0 and line[0] != "#": - stubber.modules.append(line) - gc.collect() - break - except OSError: - pass - if not stubber.modules: - stubber.modules = ["micropython"] - _log.warn("Could not find modulelist.txt, using default modules") - gc.collect() + # modules_done[modulename] = str(stubber._report[-1] if ok else "failed") + # with open("modulelist.done", "a") as f: + # f.write("{}={}\n".format(modulename, "ok" if ok else "failed")) + skip += 1 + write_skip(skip) + + print("All modules have been processed, Finalizing report") + stubber.report_end() ###PARTIALEND### diff --git a/src/stubber/codemod/_partials/modules_reader.py b/src/stubber/codemod/_partials/modules_reader.py index ec33dfee2..f28ef76c3 100644 --- a/src/stubber/codemod/_partials/modules_reader.py +++ b/src/stubber/codemod/_partials/modules_reader.py @@ -30,6 +30,9 @@ def create_all_stubs(self): def read_path() -> str: ... + def file_exists(f: str) -> bool: + ... + @type_check_only class _gc: def collect(self) -> None: @@ -41,28 +44,37 @@ def collect(self) -> None: stubber: Stubber = None # type: ignore LIBS: List[str] = [".", "lib"] + # sourcery skip: use-named-expression ###PARTIAL### # Read stubs from modulelist in the current folder or in /libs # fall back to default modules -stubber.modules = [] # avoid duplicates -for p in LIBS: - try: - _1 = gc.mem_free() # type: ignore - with open(p + "/modulelist.txt") as f: - print("Debug: List of modules: " + p + "/modulelist.txt") - line = f.readline() - while line: - line = line.strip() +def get_modulelist(stubber): + # new + gc.collect() + stubber.modules = [] # avoid duplicates + for p in LIBS: + fname = p + "/modulelist.txt" + if not file_exists(fname): + continue + with open(fname) as f: + # print("DEBUG: list of modules: " + p + "/modulelist.txt") + while True: + line = f.readline().strip() + if not line: + break if len(line) > 0 and line[0] != "#": stubber.modules.append(line) - line = f.readline() - gc.collect() # type: ignore - print("Debug: Used memory to load modulelist.txt: " + str(_1 - gc.mem_free()) + " bytes") # type: ignore + gc.collect() + print("BREAK") break - except Exception: - pass -if not stubber.modules: - stubber.modules = ["micropython"] - print("Could not find modulelist.txt, using default modules") + + if not stubber.modules: + stubber.modules = ["micropython"] + # _log.warn("Could not find modulelist.txt, using default modules") + gc.collect() + + +stubber.modules = [] # avoid duplicates +get_modulelist(stubber) ###PARTIALEND### diff --git a/src/stubber/codemod/board.py b/src/stubber/codemod/board.py index 19f9b8535..6812c13aa 100644 --- a/src/stubber/codemod/board.py +++ b/src/stubber/codemod/board.py @@ -12,10 +12,10 @@ from libcst.codemod._codemod import Codemod # type: ignore from packaging.version import Version +from stubber import __version__ from stubber.codemod._partials import Partial from stubber.codemod.modify_list import ListChangeSet, ModifyListElements from stubber.cst_transformer import update_module_docstr -from stubber import __version__ # matches on `stubber = Stubber()` _STUBBER_MATCHER = m.Assign( @@ -146,9 +146,7 @@ class ModuleDocCodemod(codemod.Codemod): def __init__(self, context: codemod.CodemodContext, module_doc: str): super().__init__(context) if module_doc.endswith('"""\n'): - generated = ( - f'\nThis variant was generated from createstubs.py by micropython-stubber v{Version(__version__).base_version}\n"""\n' - ) + generated = f'\nThis variant was generated from createstubs.py by micropython-stubber v{Version(__version__).base_version}\n"""\n' module_doc = module_doc[:-4] + generated self.module_doc = module_doc diff --git a/src/stubber/codemod/enrich.py b/src/stubber/codemod/enrich.py index 8f3cc52f9..e3a5a98fe 100644 --- a/src/stubber/codemod/enrich.py +++ b/src/stubber/codemod/enrich.py @@ -74,7 +74,7 @@ def enrich_file( log.debug(f"Merge {target_path} from {docstub_file}") # read source file - old_code = target_path.read_text() + old_code = target_path.read_text(encoding="utf-8") codemod_instance = merge_docstub.MergeCommand(context, docstub_file=docstub_file) if not ( diff --git a/src/stubber/commands/build_cmd.py b/src/stubber/commands/build_cmd.py index 01dc4e472..5e1b3ce80 100644 --- a/src/stubber/commands/build_cmd.py +++ b/src/stubber/commands/build_cmd.py @@ -22,7 +22,7 @@ multiple=True, default=[CONFIG.stable_version], show_default=True, - help="multiple: ", + help="MicroPython version to build, or 'preview' for the latest preview version", ) @click.option( "--port", @@ -72,9 +72,11 @@ def cli_build( ports = list(ports) boards = list(boards) - if len(versions) > 1 : - raise NotImplementedError("Multiple versions are not supported yet\n See https://github.com/Josverl/micropython-stubber/issues/487") - + if len(versions) > 1: + raise NotImplementedError( + "Multiple versions are not supported yet\n See https://github.com/Josverl/micropython-stubber/issues/487" + ) + # db = get_database(publish_path=CONFIG.publish_path, production=production) log.info(f"Build {family} {versions} {ports} {boards}") diff --git a/src/stubber/commands/get_docstubs_cmd.py b/src/stubber/commands/get_docstubs_cmd.py index da8050012..a28e5c9dd 100644 --- a/src/stubber/commands/get_docstubs_cmd.py +++ b/src/stubber/commands/get_docstubs_cmd.py @@ -39,9 +39,7 @@ show_default=True, ) # @click.option("--family", "-f", "basename", default="micropython", help="Micropython family.", show_default=True) -@click.option( - "--version", "--tag", default="", type=str, help="Version number to use. [default: Git tag]" -) +@click.option("--version", "--tag", default="", type=str, help="Version number to use. [default: Git tag]") @click.option("--black/--no-black", "-b/-nb", default=True, help="Run black", show_default=True) @click.pass_context def cli_docstubs( @@ -72,16 +70,13 @@ def cli_docstubs( result = fetch_repos(version, CONFIG.mpy_path, CONFIG.mpy_lib_path) if not result: return -1 + # get the current checked out version + version = utils.checkedout_version(CONFIG.mpy_path) - v_tag = git.get_local_tag(rst_path.as_posix()) - if not v_tag: - # if we can't find a tag , bail - raise ValueError("No valid Tag found") - v_tag = utils.clean_version(v_tag, flat=True, drop_v=False) release = git.get_local_tag(rst_path.as_posix(), abbreviate=False) or "" - dst_path = Path(target) / f"{basename}-{v_tag}-docstubs" + dst_path = Path(target) / f"{basename}-{version}-docstubs" - log.info(f"Get docstubs for MicroPython {utils.clean_version(v_tag, drop_v=False)}") - generate_from_rst(rst_path, dst_path, v_tag, release=release, suffix=".pyi",black=black) + log.info(f"Get docstubs for MicroPython {utils.clean_version(version, drop_v=False)}") + generate_from_rst(rst_path, dst_path, version, release=release, suffix=".pyi", black=black) log.info("::group:: Done") diff --git a/src/stubber/commands/get_frozen_cmd.py b/src/stubber/commands/get_frozen_cmd.py index 5528ac339..a4237a214 100644 --- a/src/stubber/commands/get_frozen_cmd.py +++ b/src/stubber/commands/get_frozen_cmd.py @@ -36,6 +36,7 @@ default="", # default=[CONFIG.stable_version], show_default=True, + help="The version of MicroPython to get the frozen modules for. Use 'preview' to get the latest version from the micropython repo", ) @click.option( "--stubgen/--no-stubgen", @@ -78,27 +79,21 @@ def cli_get_frozen( else: version = utils.clean_version(git.get_local_tag(CONFIG.mpy_path.as_posix()) or "0.0") if not version: - log.warning( - "Unable to find the micropython repo in folder : {}".format(CONFIG.mpy_path.as_posix()) - ) + log.warning("Unable to find the micropython repo in folder : {}".format(CONFIG.mpy_path.as_posix())) log.info("MicroPython version : {}".format(version)) # folder/{family}-{version}-frozen family = "micropython" - stub_path = Path(stub_folder) / f"{family}-{utils.clean_version(version, flat=True)}-frozen" + + stub_path = freeze_any(version=version, mpy_path=CONFIG.mpy_path, mpy_lib_path=CONFIG.mpy_lib_path) stub_paths.append(stub_path) - freeze_any( - stub_path, version=version, mpy_path=CONFIG.mpy_path, mpy_lib_path=CONFIG.mpy_lib_path - ) # Also enrich the frozen modules from the doc stubs if available # first create .pyi files so they can be enriched utils.do_post_processing(stub_paths, stubgen=stubgen, black=False, autoflake=False) family = "micropython" - docstubs_path = ( - Path(CONFIG.stub_path) - / f"{family}-{utils.clean_version(version, drop_v=False, flat=True)}-docstubs" - ) + _version = utils.clean_version(version, drop_v=False, flat=True) + docstubs_path = Path(CONFIG.stub_path) / f"{family}-{_version}-docstubs" if docstubs_path.exists(): log.info(f"Enriching {str(stub_path)} with {docstubs_path}") if merged := enrich_folder( diff --git a/src/stubber/commands/switch_cmd.py b/src/stubber/commands/switch_cmd.py index 52c888840..0f8e31992 100644 --- a/src/stubber/commands/switch_cmd.py +++ b/src/stubber/commands/switch_cmd.py @@ -11,6 +11,7 @@ import stubber.basicgit as git from stubber.utils.config import CONFIG from stubber.utils.repos import fetch_repos, repo_paths +from stubber.utils.versions import SET_PREVIEW, V_PREVIEW from .cli import stubber_cli @@ -22,10 +23,10 @@ # get version list from Git tags in the repo that is provided on the command line try: - VERSION_LIST = git.get_tags("micropython/micropython", minver="v1.9.3") + ["latest"] + VERSION_LIST = git.get_tags("micropython/micropython", minver="v1.9.3") + [V_PREVIEW, "latest"] except Exception: # offline fallback - VERSION_LIST = ["v1.91.1", "v1.20.0", "latest"] + VERSION_LIST = ["v1.91.1", "v1.20.1", "v1.21.0", "v1.22.1", "preview"] @stubber_cli.command(name="switch") @@ -46,7 +47,8 @@ def cli_switch(path: Union[str, Path], tag: Optional[str] = None): mpy_path, mpy_lib_path = repo_paths(Path(path)) except Exception: return -1 - if not tag: - tag = "latest" + if not tag or tag in SET_PREVIEW: + tag = V_PREVIEW + result = fetch_repos(tag, mpy_path, mpy_lib_path) return -1 if result else 0 diff --git a/src/stubber/data/board_info.csv b/src/stubber/data/board_info.csv index c4d8d6814..9e9cb6aaa 100644 --- a/src/stubber/data/board_info.csv +++ b/src/stubber/data/board_info.csv @@ -1,113 +1,101 @@ board,description -LaunchPad with CC3200,LAUNCHXL -WiPy with CC3200,WIPY +4MB/OTA module with ESP32,GENERIC_OTA +Actinius Icarus with NRF9160,ACTINIUS_ICARUS +Adafruit Feather RP2040,ADAFRUIT_FEATHER_RP2040 +Adafruit Feather STM32F405 with STM32F405RG,ADAFRUIT_F405_EXPRESS +Adafruit ItsyBitsy RP2040,ADAFRUIT_ITSYBITSY_RP2040 +Adafruit Metro M7 with MIMXRT1011DAE5A,ADAFRUIT_METRO_M7 +Adafruit QT Py RP2040,ADAFRUIT_QTPY_RP2040 +Arduino GIGA R1 WiFi with STM32H747,ARDUINO_GIGA +Arduino Nano 33 BLE Sense with NRF52840,ARDUINO_NANO_33_BLE_SENSE Arduino Nano ESP32 with ESP32S3,ARDUINO_NANO_ESP32 -Generic ESP32 module with ESP32,ESP32_GENERIC +Arduino Nano RP2040 Connect,ARDUINO_NANO_RP2040_CONNECT +Arduino Nicla Vision with STM32H747,ARDUINO_NICLA_VISION +Arduino Portenta C33 with RA6M5,ARDUINO_PORTENTA_C33 +Arduino Portenta H7 with STM32H747,ARDUINO_PORTENTA_H7 +Arduino Primo with NRF52832,ARDUINO_PRIMO +B-L072Z-LRWAN1 with STM32L072CZ,B_L072Z_LRWAN1 +B-L475E-IOT01A with STM32L475,B_L475E_IOT01A +Bluefruit nRF52 Feather with NRF52832,FEATHER52 +BLUEIO-TAG-EVIM with NRF52832,BLUEIO_TAG_EVIM +Cerb40 with STM32F405RG,CERB40 +CustomPCB with STM32F439,STM32F439 +DVK-BL652 with NRF52832,DVK_BL652 +EK-RA4M1 with RA4M1,EK_RA4M1 +EK-RA4W1 with RA4W1,EK_RA4W1 +EK-RA6M1 with RA6M1,EK_RA6M1 +EK-RA6M2 with RA6M2,EK_RA6M2 +ESP module (1M) with ESP8266,ESP8266_GENERIC +ESP module (512K) with ESP8266,ESP8266_GENERIC +ESP module with ESP8266,ESP8266_GENERIC +ESP32 module (spiram) with ESP32,GENERIC_SPIRAM +ESP32 module with ESP32,GENERIC +ESP32 Unicore module with ESP32-UNICORE,GENERIC_UNICORE +ESP32-D2WD,ESP32_GENERIC +ESP32-S2-WROVER with ESP32-S2,ESP32_S2_WROVER +ESP32-UNICORE,ESP32_GENERIC ESP32C3 module with ESP32C3,ESP32_GENERIC_C3 -Generic ESP32S2 module with ESP32S2,ESP32_GENERIC_S2 -Generic ESP32S3 module with ESP32S3,ESP32_GENERIC_S3 -LILYGO TTGO LoRa32 with ESP32,LILYGO_TTGO_LORA32 -LOLIN_C3_MINI with ESP32-C3FH4,LOLIN_C3_MINI -LOLIN_S2_MINI with ESP32-S2FN4R2,LOLIN_S2_MINI -LOLIN_S2_PICO with ESP32-S2FN4R2,LOLIN_S2_PICO -M5Stack ATOM with ESP32-PICO-D4,M5STACK_ATOM -Olimex ESP32 ETH with ESP32,OLIMEX_ESP32_POE -Silicognition wESP32 with ESP32,SIL_WESP32 -FeatherS2 with ESP32-S2,UM_FEATHERS2 +ESP32S2 module with ESP32S2,GENERIC_S2 +ESP32S3 module (spiram octal) with ESP32S3,GENERIC_S3_SPIRAM_OCT +ESP32S3 module (spiram) with ESP32S3,GENERIC_S3_SPIRAM +ESP32S3 module with ESP32S3,GENERIC_S3 +Espruino Pico with STM32F401CD,ESPRUINO_PICO +EVK_NINA_B1 with NRF52832,EVK_NINA_B1 +EVK_NINA_B3 with NRF52840,EVK_NINA_B3 +F411DISC with STM32F411,STM32F411DISC +F429I-DISCO with STM32F429,STM32F429DISC +F4DISC with STM32F407,STM32F4DISC +F769DISC with STM32F769,STM32F769DISC +F7DISC with STM32F746,STM32F7DISC +Feather M0 Express with SAMD21G18A,ADAFRUIT_FEATHER_M0_EXPRESS +Feather M4 Express with SAMD51J19A,ADAFRUIT_FEATHER_M4_EXPRESS FeatherS2 Neo with ESP32-S2FN4R2,UM_FEATHERS2NEO +FeatherS2 with ESP32-S2,UM_FEATHERS2 FeatherS3 with ESP32-S3,UM_FEATHERS3 -NanoS3 with ESP32-S3-FN8,UM_NANOS3 -ProS3 with ESP32-S3,UM_PROS3 -TinyPICO with ESP32-PICO-D4,UM_TINYPICO -TinyS2 with ESP32-S2FN4R2,UM_TINYS2 -TinyS3 with ESP32-S3-FN8,UM_TINYS3 -ESP module with ESP8266,ESP8266_GENERIC -Adafruit Metro M7 with MIMXRT1011DAE5A,ADAFRUIT_METRO_M7 +GARATRONIC_PYBSTICK26_RP2040,GARATRONIC_PYBSTICK26_RP2040 +Generic ESP32 module with ESP32,ESP32_GENERIC +Generic ESP32 module with OTA,ESP32_GENERIC +Generic ESP32 module with SPIRAM,ESP32_GENERIC +Generic ESP32-D2WD module with ESP32-D2WD,GENERIC_D2WD +Generic ESP32S2 module with ESP32S2,ESP32_GENERIC_S2 +Generic ESP32S3 module with ESP32S3,ESP32_GENERIC_S3 +Generic ESP32S3 module with Octal-SPIRAM,ESP32_GENERIC_S3 +GIGA with STM32H747,ARDUINO_GIGA +HydraBus1.0 with STM32F4,HYDRABUS i.MX RT1010 EVK with MIMXRT1011DAE5A,MIMXRT1010_EVK i.MX RT1015 EVK with MIMXRT1015DAF5A,MIMXRT1015_EVK i.MX RT1020 EVK with MIMXRT1021DAG5A,MIMXRT1020_EVK +i.MX RT1050 EVK with MIMXRT1052DVL6B,MIMXRT1050_EVK +i.MX RT1050 EVKB with MIMXRT1052DVL6B,MIMXRT1050_EVKB i.MX RT1050 EVKB-A1 with MIMXRT1052DVL6B,MIMXRT1050_EVK i.MX RT1060 EVK with MIMXRT1062DVJ6A,MIMXRT1060_EVK i.MX RT1064 EVK with MIMXRT1064DVL6A,MIMXRT1064_EVK i.MX RT1170 EVK with MIMXRT1176DVMAA,MIMXRT1170_EVK -RT1010-Py-DevKIT with MIMXRT1011DAE5A,OLIMEX_RT1010 -Seeed ARCH MIX with MIMXRT1052DVL5B,SEEED_ARCH_MIX -Teensy 4.0 with MIMXRT1062DVJ6A,TEENSY40 -Teensy 4.1 with MIMXRT1062DVJ6A,TEENSY41 -Actinius Icarus with NRF9160,ACTINIUS_ICARUS -Arduino Nano 33 BLE Sense with NRF52840,ARDUINO_NANO_33_BLE_SENSE -Arduino Primo with NRF52832,ARDUINO_PRIMO -BLUEIO-TAG-EVIM with NRF52832,BLUEIO_TAG_EVIM -DVK-BL652 with NRF52832,DVK_BL652 -EVK_NINA_B1 with NRF52832,EVK_NINA_B1 -EVK_NINA_B3 with NRF52840,EVK_NINA_B3 -Bluefruit nRF52 Feather with NRF52832,FEATHER52 IBK-BLYST-NANO with NRF52832,IBK_BLYST_NANO IDK-BLYST-NANO with NRF52832,IDK_BLYST_NANO -micro:bit with NRF51822,MICROBIT -MDK-USB-DONGLE with NRF52840,NRF52840_MDK_USB_DONGLE -XENON with NRF52840,PARTICLE_XENON -PCA10000 with NRF51822,PCA10000 -PCA10001 with NRF51822,PCA10001 -PCA10028 with NRF51822,PCA10028 -PCA10031 with NRF51822,PCA10031 -PCA10040 with NRF52832,PCA10040 -PCA10056 with NRF52840,PCA10056 -PCA10059 with NRF52840,PCA10059 -PCA10090 with NRF9160,PCA10090 -XIAO nRF52840 Sense with NRF52840,SEEED_XIAO_NRF52 -WT51822-S4AT with NRF51822,WT51822_S4AT -PORTENTA C33 with RA6M5,ARDUINO_PORTENTA_C33 -EK-RA4M1 with RA4M1,EK_RA4M1 -EK-RA4W1 with RA4W1,EK_RA4W1 -EK-RA6M1 with RA6M1,EK_RA6M1 -EK-RA6M2 with RA6M2,EK_RA6M2 -RA4M1 CLICKER with RA4M1,RA4M1_CLICKER -VK-RA6M5 with RA6M5,VK_RA6M5 -Adafruit Feather RP2040,ADAFRUIT_FEATHER_RP2040 -Adafruit ItsyBitsy RP2040,ADAFRUIT_ITSYBITSY_RP2040 -Adafruit QT Py RP2040,ADAFRUIT_QTPY_RP2040 -Arduino Nano RP2040 Connect,ARDUINO_NANO_RP2040_CONNECT -GARATRONIC_PYBSTICK26_RP2040,GARATRONIC_PYBSTICK26_RP2040 -nullbits Bit-C PRO,NULLBITS_BIT_C_PRO -Pimoroni Pico LiPo 16MB,PIMORONI_PICOLIPO_16MB -Pimoroni Pico LiPo 4MB,PIMORONI_PICOLIPO_4MB -Pimoroni Tiny 2040,PIMORONI_TINY2040 -Raspberry Pi Pico,RPI_PICO -Raspberry Pi Pico W,RPI_PICO_W -SparkFun Pro Micro RP2040,SPARKFUN_PROMICRO -SparkFun Thing Plus RP2040,SPARKFUN_THINGPLUS -W5100S-EVB-Pico,W5100S_EVB_PICO -W5500-EVB-Pico,W5500_EVB_PICO -WeAct Studio RP2040,WEACTSTUDIO -Feather M0 Express with SAMD21G18A,ADAFRUIT_FEATHER_M0_EXPRESS -Feather M4 Express with SAMD51J19A,ADAFRUIT_FEATHER_M4_EXPRESS ItsyBitsy M0 Express with SAMD21G18A,ADAFRUIT_ITSYBITSY_M0_EXPRESS ItsyBitsy M4 Express with SAMD51G19A,ADAFRUIT_ITSYBITSY_M4_EXPRESS -Metro M4 Express Airlift with SAMD51J19A,ADAFRUIT_METRO_M4_EXPRESS -Trinket M0 with SAMD21E18A,ADAFRUIT_TRINKET_M0 -Mini SAM M4 with SAMD51G19A,MINISAM_M4 -SAMD21-XPLAINED-PRO with SAMD21J18A,SAMD21_XPLAINED_PRO -Wio Terminal D51R with SAMD51P19A,SEEED_WIO_TERMINAL -Seeed Xiao with SAMD21G18A,SEEED_XIAO_SAMD21 -Sparkfun SAMD51 Thing Plus with SAMD51J20A,SPARKFUN_SAMD51_THING_PLUS -Adafruit Feather STM32F405 with STM32F405RG,ADAFRUIT_F405_EXPRESS -GIGA with STM32H747,ARDUINO_GIGA -NICLAVISION with STM32H747,ARDUINO_NICLA_VISION -PORTENTA with STM32H747,ARDUINO_PORTENTA_H7 -B-L072Z-LRWAN1 with STM32L072CZ,B_L072Z_LRWAN1 -B-L475E-IOT01A with STM32L475,B_L475E_IOT01A -Cerb40 with STM32F405RG,CERB40 -Espruino Pico with STM32F401CD,ESPRUINO_PICO -NADHAT_PYBF405 with STM32F405RG,GARATRONIC_NADHAT_F405 -PYBSTICK26_STD with STM32F411RE,GARATRONIC_PYBSTICK26_F411 -HydraBus1.0 with STM32F4,HYDRABUS +L476-DISCO with STM32L476,STM32L476DISC +L496G-DISCO with STM32L496,STM32L496GDISC +LaunchPad with CC3200,LAUNCHXL LEGO Technic Hub No.6 with STM32F413,LEGO_HUB_NO6 LEGO Technic Hub No.7 with STM32F413,LEGO_HUB_NO7 +LILYGO TTGO LoRa32 with ESP32,LILYGO_TTGO_LORA32 LIMIFROG with STM32L476,LIMIFROG -MIKROE_CLICKER2_STM32 with STM32F407,MIKROE_CLICKER2_STM32 +LOLIN_C3_MINI with ESP32-C3FH4,LOLIN_C3_MINI +LOLIN_S2_MINI with ESP32-S2FN4R2,LOLIN_S2_MINI +LOLIN_S2_PICO with ESP32-S2FN4R2,LOLIN_S2_PICO +M5Stack ATOM with ESP32-PICO-D4,M5STACK_ATOM +MDK-USB-DONGLE with NRF52840,NRF52840_MDK_USB_DONGLE +Metro M4 Express Airlift with SAMD51J19A,ADAFRUIT_METRO_M4_EXPRESS +micro:bit with NRF51822,MICROBIT MikroE Quail with STM32F427VI,MIKROE_QUAIL +MIKROE_CLICKER2_STM32 with STM32F407,MIKROE_CLICKER2_STM32 +Mini SAM M4 with SAMD51G19A,MINISAM_M4 +NADHAT_PYBF405 with STM32F405RG,GARATRONIC_NADHAT_F405 +NanoS3 with ESP32-S3-FN8,UM_NANOS3 NetduinoPlus2 with STM32F405RG,NETDUINO_PLUS_2 +NICLAVISION with STM32H747,ARDUINO_NICLA_VISION NUCLEO-F091RC with STM32F091RCT6,NUCLEO_F091RC NUCLEO-F401RE with STM32F401xE,NUCLEO_F401RE NUCLEO-F411RE with STM32F411xE,NUCLEO_F411RE @@ -121,10 +109,6 @@ NUCLEO-F746ZG with STM32F746,NUCLEO_F746ZG NUCLEO-F756ZG with STM32F756,NUCLEO_F756ZG NUCLEO-F767ZI with STM32F767,NUCLEO_F767ZI NUCLEO-G0B1RE with STM32G0B1xE,NUCLEO_G0B1RE -NUCLEO_G474RE with STM32G474,NUCLEO_G474RE -NUCLEO_H723ZG with STM32H723ZGT6,NUCLEO_H723ZG -NUCLEO_H743ZI with STM32H743,NUCLEO_H743ZI -NUCLEO_H743ZI2,NUCLEO_H743ZI2 NUCLEO-L073RZ with STM32L073RZT6,NUCLEO_L073RZ NUCLEO-L152RE with STM32L152xE,NUCLEO_L152RE NUCLEO-L432KC with STM32L432KC,NUCLEO_L432KC @@ -133,28 +117,77 @@ NUCLEO-L476RG with STM32L476RG,NUCLEO_L476RG NUCLEO-L4A6ZG with STM32L4A6ZG,NUCLEO_L4A6ZG NUCLEO-WB55 with STM32WB55RGV6,NUCLEO_WB55 NUCLEO-WL55 with STM32WL55JCI7,NUCLEO_WL55 +NUCLEO_G474RE with STM32G474,NUCLEO_G474RE +NUCLEO_H563ZI with STM32H563ZI,NUCLEO_H563ZI +NUCLEO_H723ZG with STM32H723ZGT6,NUCLEO_H723ZG +NUCLEO_H743ZI with STM32H743,NUCLEO_H743ZI +NUCLEO_H743ZI2,NUCLEO_H743ZI2 +nullbits Bit-C PRO,NULLBITS_BIT_C_PRO +Olimex ESP32 ETH with ESP32,OLIMEX_ESP32_POE OLIMEX STM32-E407 with STM32F407,OLIMEX_E407 OLIMEX STM32-H407 with STM32F407,OLIMEX_H407 +PCA10000 with NRF51822,PCA10000 +PCA10001 with NRF51822,PCA10001 +PCA10028 with NRF51822,PCA10028 +PCA10031 with NRF51822,PCA10031 +PCA10040 with NRF52832,PCA10040 +PCA10056 with NRF52840,PCA10056 +PCA10059 with NRF52840,PCA10059 +PCA10090 with NRF9160,PCA10090 +Pimoroni Pico LiPo 16MB,PIMORONI_PICOLIPO_16MB +Pimoroni Pico LiPo 4MB,PIMORONI_PICOLIPO_4MB +Pimoroni Tiny 2040,PIMORONI_TINY2040 +Pololu 3pi+ 2040 Robot,POLOLU_3PI_2040_ROBOT +Pololu Zumo 2040 Robot,POLOLU_ZUMO_2040_ROBOT +PORTENTA C33 with RA6M5,ARDUINO_PORTENTA_C33 +PORTENTA with STM32H747,ARDUINO_PORTENTA_H7 +ProS3 with ESP32-S3,UM_PROS3 PYBD-SF2W with STM32F722IEK,PYBD_SF2 PYBD-SF3W with STM32F733IEK,PYBD_SF3 PYBD-SF6W with STM32F767IIK,PYBD_SF6 PYBLITEv1.0 with STM32F411RE,PYBLITEV10 +PYBSTICK26_STD with STM32F411RE,GARATRONIC_PYBSTICK26_F411 PYBv1.0 with STM32F405RG,PYBV10 PYBv1.1 with STM32F405RG,PYBV11 PYBv3 with STM32F405RG,PYBV3 PYBv4 with STM32F405RG,PYBV4 +RA4M1 CLICKER with RA4M1,RA4M1_CLICKER +RA4M1_CLICKER with RA4M1,RA4M1_CLICKER +RA4M1_EK with RA4M1,RA4M1_EK +RA4W1_EK with RA4W1,RA4W1_EK +RA6M1_EK with RA6M1,RA6M1_EK +RA6M2_EK with RA6M2,RA6M2_EK +Raspberry Pi Pico,RPI_PICO +Raspberry Pi Pico W,RPI_PICO_W +RT1010-Py-DevKIT with MIMXRT1011DAE5A,OLIMEX_RT1010 +SAMD21-XPLAINED-PRO with SAMD21J18A,SAMD21_XPLAINED_PRO +Seeed ARCH MIX with MIMXRT1052DVL5B,SEEED_ARCH_MIX +Seeed Xiao with SAMD21G18A,SEEED_XIAO_SAMD21 +Silicognition RP2040-Shim,SIL_RP2040_SHIM +Silicognition wESP32 with ESP32,SIL_WESP32 +SparkFun Pro Micro RP2040,SPARKFUN_PROMICRO +Sparkfun SAMD51 Thing Plus with SAMD51J20A,SPARKFUN_SAMD51_THING_PLUS SparkFun STM32 MicroMod Processor with STM32F405RG,SPARKFUN_MICROMOD_STM32 -F411DISC with STM32F411,STM32F411DISC -F429I-DISCO with STM32F429,STM32F429DISC -CustomPCB with STM32F439,STM32F439 -F4DISC with STM32F407,STM32F4DISC -F769DISC with STM32F769,STM32F769DISC -F7DISC with STM32F746,STM32F7DISC +SparkFun Thing Plus RP2040,SPARKFUN_THINGPLUS STM32H573I-DK with STM32H573IIK3Q,STM32H573I_DK STM32H7B3I-DK with STM32H7B3LIH6Q,STM32H7B3I_DK -L476-DISCO with STM32L476,STM32L476DISC -L496G-DISCO with STM32L496,STM32L496GDISC +Teensy 4.0 with MIMXRT1062DVJ6A,TEENSY40 +Teensy 4.1 with MIMXRT1062DVJ6A,TEENSY41 +TinyPICO with ESP32-PICO-D4,UM_TINYPICO +TinyS2 with ESP32-S2FN4R2,UM_TINYS2 +TinyS3 with ESP32-S3-FN8,UM_TINYS3 +TinyWATCH S3 with ESP32-S3-PICO-1-N8R2,UM_TINYWATCHS3 +Trinket M0 with SAMD21E18A,ADAFRUIT_TRINKET_M0 USBDongle-WB55 with STM32WB55CGU6,USBDONGLE_WB55 VCC-GND STM32F407VE with STM32F407VE,VCC_GND_F407VE VCC-GND STM32F407ZG with STM32F407ZG,VCC_GND_F407ZG VCC-GND STM32H743VI with STM32H743VI,VCC_GND_H743VI +VK-RA6M5 with RA6M5,VK_RA6M5 +W5100S-EVB-Pico,W5100S_EVB_PICO +W5500-EVB-Pico,W5500_EVB_PICO +WeAct Studio RP2040,WEACTSTUDIO +Wio Terminal D51R with SAMD51P19A,SEEED_WIO_TERMINAL +WiPy with CC3200,WIPY +WT51822-S4AT with NRF51822,WT51822_S4AT +XENON with NRF52840,PARTICLE_XENON +XIAO nRF52840 Sense with NRF52840,SEEED_XIAO_NRF52 diff --git a/src/stubber/data/board_info.json b/src/stubber/data/board_info.json index 640d848c3..9985acf6f 100644 --- a/src/stubber/data/board_info.json +++ b/src/stubber/data/board_info.json @@ -1,1274 +1,1730 @@ [ { - "port": "cc3200", - "board": "LAUNCHXL", - "board_name": "LaunchPad", - "mcu_name": "CC3200", - "description": "LaunchPad with CC3200", - "path": "repos/micropython/ports/cc3200/boards/LAUNCHXL/mpconfigboard.h" + "description": "4MB/OTA module with ESP32", + "port": "esp32", + "board": "GENERIC_OTA", + "board_name": "4MB/OTA module", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/GENERIC_OTA/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "cc3200", - "board": "WIPY", - "board_name": "WiPy", - "mcu_name": "CC3200", - "description": "WiPy with CC3200", - "path": "repos/micropython/ports/cc3200/boards/WIPY/mpconfigboard.h" + "description": "Actinius Icarus with NRF9160", + "port": "nrf", + "board": "ACTINIUS_ICARUS", + "board_name": "Actinius Icarus", + "mcu_name": "NRF9160", + "path": "ports/nrf/boards/ACTINIUS_ICARUS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "ARDUINO_NANO_ESP32", - "board_name": "Arduino Nano ESP32", - "mcu_name": "ESP32S3", - "description": "Arduino Nano ESP32 with ESP32S3", - "path": "repos/micropython/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h" + "description": "Adafruit Feather RP2040", + "port": "rp2", + "board": "ADAFRUIT_FEATHER_RP2040", + "board_name": "Adafruit Feather RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "ESP32_GENERIC", - "board_name": "Generic ESP32 module", - "mcu_name": "ESP32", - "description": "Generic ESP32 module with ESP32", - "path": "repos/micropython/ports/esp32/boards/ESP32_GENERIC/mpconfigboard.h" + "description": "Adafruit Feather STM32F405 with STM32F405RG", + "port": "stm32", + "board": "ADAFRUIT_F405_EXPRESS", + "board_name": "Adafruit Feather STM32F405", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "ESP32_GENERIC_C3", - "board_name": "ESP32C3 module", - "mcu_name": "ESP32C3", - "description": "ESP32C3 module with ESP32C3", - "path": "repos/micropython/ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h" + "description": "Adafruit ItsyBitsy RP2040", + "port": "rp2", + "board": "ADAFRUIT_ITSYBITSY_RP2040", + "board_name": "Adafruit ItsyBitsy RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "ESP32_GENERIC_S2", - "board_name": "Generic ESP32S2 module", - "mcu_name": "ESP32S2", - "description": "Generic ESP32S2 module with ESP32S2", - "path": "repos/micropython/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h" + "description": "Adafruit Metro M7 with MIMXRT1011DAE5A", + "port": "mimxrt", + "board": "ADAFRUIT_METRO_M7", + "board_name": "Adafruit Metro M7", + "mcu_name": "MIMXRT1011DAE5A", + "path": "ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "ESP32_GENERIC_S3", - "board_name": "Generic ESP32S3 module", - "mcu_name": "ESP32S3", - "description": "Generic ESP32S3 module with ESP32S3", - "path": "repos/micropython/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.h" + "description": "Adafruit QT Py RP2040", + "port": "rp2", + "board": "ADAFRUIT_QTPY_RP2040", + "board_name": "Adafruit QT Py RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "LILYGO_TTGO_LORA32", - "board_name": "LILYGO TTGO LoRa32", - "mcu_name": "ESP32", - "description": "LILYGO TTGO LoRa32 with ESP32", - "path": "repos/micropython/ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.h" + "description": "Arduino GIGA R1 WiFi with STM32H747", + "port": "stm32", + "board": "ARDUINO_GIGA", + "board_name": "Arduino GIGA R1 WiFi", + "mcu_name": "STM32H747", + "path": "ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "LOLIN_C3_MINI", - "board_name": "LOLIN_C3_MINI", - "mcu_name": "ESP32-C3FH4", - "description": "LOLIN_C3_MINI with ESP32-C3FH4", - "path": "repos/micropython/ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h" + "description": "Arduino Nano 33 BLE Sense with NRF52840", + "port": "nrf", + "board": "ARDUINO_NANO_33_BLE_SENSE", + "board_name": "Arduino Nano 33 BLE Sense", + "mcu_name": "NRF52840", + "path": "ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "Arduino Nano ESP32 with ESP32S3", "port": "esp32", - "board": "LOLIN_S2_MINI", - "board_name": "LOLIN_S2_MINI", - "mcu_name": "ESP32-S2FN4R2", - "description": "LOLIN_S2_MINI with ESP32-S2FN4R2", - "path": "repos/micropython/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h" + "board": "ARDUINO_NANO_ESP32", + "board_name": "Arduino Nano ESP32", + "mcu_name": "ESP32S3", + "path": "ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "LOLIN_S2_PICO", - "board_name": "LOLIN_S2_PICO", - "mcu_name": "ESP32-S2FN4R2", - "description": "LOLIN_S2_PICO with ESP32-S2FN4R2", - "path": "repos/micropython/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h" + "description": "Arduino Nano RP2040 Connect", + "port": "rp2", + "board": "ARDUINO_NANO_RP2040_CONNECT", + "board_name": "Arduino Nano RP2040 Connect", + "mcu_name": "-", + "path": "ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "M5STACK_ATOM", - "board_name": "M5Stack ATOM", - "mcu_name": "ESP32-PICO-D4", - "description": "M5Stack ATOM with ESP32-PICO-D4", - "path": "repos/micropython/ports/esp32/boards/M5STACK_ATOM/mpconfigboard.h" + "description": "Arduino Nicla Vision with STM32H747", + "port": "stm32", + "board": "ARDUINO_NICLA_VISION", + "board_name": "Arduino Nicla Vision", + "mcu_name": "STM32H747", + "path": "ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "OLIMEX_ESP32_POE", - "board_name": "Olimex ESP32 ETH", - "mcu_name": "ESP32", - "description": "Olimex ESP32 ETH with ESP32", - "path": "repos/micropython/ports/esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.h" + "description": "Arduino Portenta C33 with RA6M5", + "port": "renesas-ra", + "board": "ARDUINO_PORTENTA_C33", + "board_name": "Arduino Portenta C33", + "mcu_name": "RA6M5", + "path": "ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "SIL_WESP32", - "board_name": "Silicognition wESP32", - "mcu_name": "ESP32", - "description": "Silicognition wESP32 with ESP32", - "path": "repos/micropython/ports/esp32/boards/SIL_WESP32/mpconfigboard.h" + "description": "Arduino Portenta H7 with STM32H747", + "port": "stm32", + "board": "ARDUINO_PORTENTA_H7", + "board_name": "Arduino Portenta H7", + "mcu_name": "STM32H747", + "path": "ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_FEATHERS2", - "board_name": "FeatherS2", - "mcu_name": "ESP32-S2", - "description": "FeatherS2 with ESP32-S2", - "path": "repos/micropython/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h" + "description": "Arduino Primo with NRF52832", + "port": "nrf", + "board": "ARDUINO_PRIMO", + "board_name": "Arduino Primo", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/ARDUINO_PRIMO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_FEATHERS2NEO", - "board_name": "FeatherS2 Neo", - "mcu_name": "ESP32-S2FN4R2", - "description": "FeatherS2 Neo with ESP32-S2FN4R2", - "path": "repos/micropython/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h" + "description": "B-L072Z-LRWAN1 with STM32L072CZ", + "port": "stm32", + "board": "B_L072Z_LRWAN1", + "board_name": "B-L072Z-LRWAN1", + "mcu_name": "STM32L072CZ", + "path": "ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_FEATHERS3", - "board_name": "FeatherS3", - "mcu_name": "ESP32-S3", - "description": "FeatherS3 with ESP32-S3", - "path": "repos/micropython/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h" + "description": "B-L475E-IOT01A with STM32L475", + "port": "stm32", + "board": "B_L475E_IOT01A", + "board_name": "B-L475E-IOT01A", + "mcu_name": "STM32L475", + "path": "ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_NANOS3", - "board_name": "NanoS3", - "mcu_name": "ESP32-S3-FN8", - "description": "NanoS3 with ESP32-S3-FN8", - "path": "repos/micropython/ports/esp32/boards/UM_NANOS3/mpconfigboard.h" + "description": "Bluefruit nRF52 Feather with NRF52832", + "port": "nrf", + "board": "FEATHER52", + "board_name": "Bluefruit nRF52 Feather", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/FEATHER52/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_PROS3", - "board_name": "ProS3", - "mcu_name": "ESP32-S3", - "description": "ProS3 with ESP32-S3", - "path": "repos/micropython/ports/esp32/boards/UM_PROS3/mpconfigboard.h" + "description": "BLUEIO-TAG-EVIM with NRF52832", + "port": "nrf", + "board": "BLUEIO_TAG_EVIM", + "board_name": "BLUEIO-TAG-EVIM", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/BLUEIO_TAG_EVIM/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_TINYPICO", - "board_name": "TinyPICO", - "mcu_name": "ESP32-PICO-D4", - "description": "TinyPICO with ESP32-PICO-D4", - "path": "repos/micropython/ports/esp32/boards/UM_TINYPICO/mpconfigboard.h" + "description": "Cerb40 with STM32F405RG", + "port": "stm32", + "board": "CERB40", + "board_name": "Cerb40", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/CERB40/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_TINYS2", - "board_name": "TinyS2", - "mcu_name": "ESP32-S2FN4R2", - "description": "TinyS2 with ESP32-S2FN4R2", - "path": "repos/micropython/ports/esp32/boards/UM_TINYS2/mpconfigboard.h" + "description": "CustomPCB with STM32F439", + "port": "stm32", + "board": "STM32F439", + "board_name": "CustomPCB", + "mcu_name": "STM32F439", + "path": "ports/stm32/boards/STM32F439/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp32", - "board": "UM_TINYS3", - "board_name": "TinyS3", - "mcu_name": "ESP32-S3-FN8", - "description": "TinyS3 with ESP32-S3-FN8", - "path": "repos/micropython/ports/esp32/boards/UM_TINYS3/mpconfigboard.h" + "description": "DVK-BL652 with NRF52832", + "port": "nrf", + "board": "DVK_BL652", + "board_name": "DVK-BL652", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/DVK_BL652/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "esp8266", - "board": "ESP8266_GENERIC", - "board_name": "ESP module", - "mcu_name": "ESP8266", - "description": "ESP module with ESP8266", - "path": "repos/micropython/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h" + "description": "EK-RA4M1 with RA4M1", + "port": "renesas-ra", + "board": "EK_RA4M1", + "board_name": "EK-RA4M1", + "mcu_name": "RA4M1", + "path": "ports/renesas-ra/boards/EK_RA4M1/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "ADAFRUIT_METRO_M7", - "board_name": "Adafruit Metro M7", - "mcu_name": "MIMXRT1011DAE5A", - "description": "Adafruit Metro M7 with MIMXRT1011DAE5A", - "path": "repos/micropython/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.h" + "description": "EK-RA4W1 with RA4W1", + "port": "renesas-ra", + "board": "EK_RA4W1", + "board_name": "EK-RA4W1", + "mcu_name": "RA4W1", + "path": "ports/renesas-ra/boards/EK_RA4W1/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "MIMXRT1010_EVK", - "board_name": "i.MX RT1010 EVK", - "mcu_name": "MIMXRT1011DAE5A", - "description": "i.MX RT1010 EVK with MIMXRT1011DAE5A", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h" + "description": "EK-RA6M1 with RA6M1", + "port": "renesas-ra", + "board": "EK_RA6M1", + "board_name": "EK-RA6M1", + "mcu_name": "RA6M1", + "path": "ports/renesas-ra/boards/EK_RA6M1/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "MIMXRT1015_EVK", - "board_name": "i.MX RT1015 EVK", - "mcu_name": "MIMXRT1015DAF5A", - "description": "i.MX RT1015 EVK with MIMXRT1015DAF5A", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h" + "description": "EK-RA6M2 with RA6M2", + "port": "renesas-ra", + "board": "EK_RA6M2", + "board_name": "EK-RA6M2", + "mcu_name": "RA6M2", + "path": "ports/renesas-ra/boards/EK_RA6M2/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "MIMXRT1020_EVK", - "board_name": "i.MX RT1020 EVK", - "mcu_name": "MIMXRT1021DAG5A", - "description": "i.MX RT1020 EVK with MIMXRT1021DAG5A", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h" + "description": "ESP module (1M) with ESP8266", + "port": "esp8266", + "board": "ESP8266_GENERIC", + "board_name": "ESP module (1M)", + "mcu_name": "ESP8266", + "path": "ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "MIMXRT1050_EVK", - "board_name": "i.MX RT1050 EVKB-A1", - "mcu_name": "MIMXRT1052DVL6B", - "description": "i.MX RT1050 EVKB-A1 with MIMXRT1052DVL6B", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h" + "description": "ESP module (512K) with ESP8266", + "port": "esp8266", + "board": "ESP8266_GENERIC", + "board_name": "ESP module (512K)", + "mcu_name": "ESP8266", + "path": "ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "MIMXRT1060_EVK", - "board_name": "i.MX RT1060 EVK", - "mcu_name": "MIMXRT1062DVJ6A", - "description": "i.MX RT1060 EVK with MIMXRT1062DVJ6A", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h" + "description": "ESP module with ESP8266", + "port": "esp8266", + "board": "ESP8266_GENERIC", + "board_name": "ESP module", + "mcu_name": "ESP8266", + "path": "ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "MIMXRT1064_EVK", - "board_name": "i.MX RT1064 EVK", - "mcu_name": "MIMXRT1064DVL6A", - "description": "i.MX RT1064 EVK with MIMXRT1064DVL6A", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h" + "description": "ESP32 module (spiram) with ESP32", + "port": "esp32", + "board": "GENERIC_SPIRAM", + "board_name": "ESP32 module (spiram)", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "mimxrt", - "board": "MIMXRT1170_EVK", - "board_name": "i.MX RT1170 EVK", - "mcu_name": "MIMXRT1176DVMAA", - "description": "i.MX RT1170 EVK with MIMXRT1176DVMAA", - "path": "repos/micropython/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h" + "description": "ESP32 module with ESP32", + "port": "esp32", + "board": "GENERIC", + "board_name": "ESP32 module", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/GENERIC/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "mimxrt", - "board": "OLIMEX_RT1010", - "board_name": "RT1010-Py-DevKIT", - "mcu_name": "MIMXRT1011DAE5A", - "description": "RT1010-Py-DevKIT with MIMXRT1011DAE5A", - "path": "repos/micropython/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h" + "description": "ESP32 Unicore module with ESP32-UNICORE", + "port": "esp32", + "board": "GENERIC_UNICORE", + "board_name": "ESP32 Unicore module", + "mcu_name": "ESP32-UNICORE", + "path": "ports/esp32/boards/GENERIC_UNICORE/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "mimxrt", - "board": "SEEED_ARCH_MIX", - "board_name": "Seeed ARCH MIX", - "mcu_name": "MIMXRT1052DVL5B", - "description": "Seeed ARCH MIX with MIMXRT1052DVL5B", - "path": "repos/micropython/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h" + "description": "ESP32-D2WD", + "port": "esp32", + "board": "ESP32_GENERIC", + "board_name": "-", + "mcu_name": "-", + "path": "ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake", + "version": "v1.22.0" }, { - "port": "mimxrt", - "board": "TEENSY40", - "board_name": "Teensy 4.0", - "mcu_name": "MIMXRT1062DVJ6A", - "description": "Teensy 4.0 with MIMXRT1062DVJ6A", - "path": "repos/micropython/ports/mimxrt/boards/TEENSY40/mpconfigboard.h" + "description": "ESP32-S2-WROVER with ESP32-S2", + "port": "esp32", + "board": "ESP32_S2_WROVER", + "board_name": "ESP32-S2-WROVER", + "mcu_name": "ESP32-S2", + "path": "ports/esp32/boards/ESP32_S2_WROVER/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "mimxrt", - "board": "TEENSY41", - "board_name": "Teensy 4.1", - "mcu_name": "MIMXRT1062DVJ6A", - "description": "Teensy 4.1 with MIMXRT1062DVJ6A", - "path": "repos/micropython/ports/mimxrt/boards/TEENSY41/mpconfigboard.h" + "description": "ESP32-UNICORE", + "port": "esp32", + "board": "ESP32_GENERIC", + "board_name": "-", + "mcu_name": "-", + "path": "ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "ACTINIUS_ICARUS", - "board_name": "Actinius Icarus", - "mcu_name": "NRF9160", - "description": "Actinius Icarus with NRF9160", - "path": "repos/micropython/ports/nrf/boards/ACTINIUS_ICARUS/mpconfigboard.h" + "description": "ESP32C3 module with ESP32C3", + "port": "esp32", + "board": "ESP32_GENERIC_C3", + "board_name": "ESP32C3 module", + "mcu_name": "ESP32C3", + "path": "ports/esp32/boards/ESP32_GENERIC_C3/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "ARDUINO_NANO_33_BLE_SENSE", - "board_name": "Arduino Nano 33 BLE Sense", - "mcu_name": "NRF52840", - "description": "Arduino Nano 33 BLE Sense with NRF52840", - "path": "repos/micropython/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/mpconfigboard.h" + "description": "ESP32S2 module with ESP32S2", + "port": "esp32", + "board": "GENERIC_S2", + "board_name": "ESP32S2 module", + "mcu_name": "ESP32S2", + "path": "ports/esp32/boards/GENERIC_S2/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "nrf", - "board": "ARDUINO_PRIMO", - "board_name": "Arduino Primo", - "mcu_name": "NRF52832", - "description": "Arduino Primo with NRF52832", - "path": "repos/micropython/ports/nrf/boards/ARDUINO_PRIMO/mpconfigboard.h" + "description": "ESP32S3 module (spiram octal) with ESP32S3", + "port": "esp32", + "board": "GENERIC_S3_SPIRAM_OCT", + "board_name": "ESP32S3 module (spiram octal)", + "mcu_name": "ESP32S3", + "path": "ports/esp32/boards/GENERIC_S3_SPIRAM_OCT/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "nrf", - "board": "BLUEIO_TAG_EVIM", - "board_name": "BLUEIO-TAG-EVIM", - "mcu_name": "NRF52832", - "description": "BLUEIO-TAG-EVIM with NRF52832", - "path": "repos/micropython/ports/nrf/boards/BLUEIO_TAG_EVIM/mpconfigboard.h" + "description": "ESP32S3 module (spiram) with ESP32S3", + "port": "esp32", + "board": "GENERIC_S3_SPIRAM", + "board_name": "ESP32S3 module (spiram)", + "mcu_name": "ESP32S3", + "path": "ports/esp32/boards/GENERIC_S3_SPIRAM/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "nrf", - "board": "DVK_BL652", - "board_name": "DVK-BL652", - "mcu_name": "NRF52832", - "description": "DVK-BL652 with NRF52832", - "path": "repos/micropython/ports/nrf/boards/DVK_BL652/mpconfigboard.h" + "description": "ESP32S3 module with ESP32S3", + "port": "esp32", + "board": "GENERIC_S3", + "board_name": "ESP32S3 module", + "mcu_name": "ESP32S3", + "path": "ports/esp32/boards/GENERIC_S3/mpconfigboard.h", + "version": "v1.20.0" + }, + { + "description": "Espruino Pico with STM32F401CD", + "port": "stm32", + "board": "ESPRUINO_PICO", + "board_name": "Espruino Pico", + "mcu_name": "STM32F401CD", + "path": "ports/stm32/boards/ESPRUINO_PICO/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "EVK_NINA_B1 with NRF52832", "port": "nrf", "board": "EVK_NINA_B1", "board_name": "EVK_NINA_B1", "mcu_name": "NRF52832", - "description": "EVK_NINA_B1 with NRF52832", - "path": "repos/micropython/ports/nrf/boards/EVK_NINA_B1/mpconfigboard.h" + "path": "ports/nrf/boards/EVK_NINA_B1/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "EVK_NINA_B3 with NRF52840", "port": "nrf", "board": "EVK_NINA_B3", "board_name": "EVK_NINA_B3", "mcu_name": "NRF52840", - "description": "EVK_NINA_B3 with NRF52840", - "path": "repos/micropython/ports/nrf/boards/EVK_NINA_B3/mpconfigboard.h" + "path": "ports/nrf/boards/EVK_NINA_B3/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "FEATHER52", - "board_name": "Bluefruit nRF52 Feather", - "mcu_name": "NRF52832", - "description": "Bluefruit nRF52 Feather with NRF52832", - "path": "repos/micropython/ports/nrf/boards/FEATHER52/mpconfigboard.h" + "description": "F411DISC with STM32F411", + "port": "stm32", + "board": "STM32F411DISC", + "board_name": "F411DISC", + "mcu_name": "STM32F411", + "path": "ports/stm32/boards/STM32F411DISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "IBK_BLYST_NANO", - "board_name": "IBK-BLYST-NANO", - "mcu_name": "NRF52832", - "description": "IBK-BLYST-NANO with NRF52832", - "path": "repos/micropython/ports/nrf/boards/IBK_BLYST_NANO/mpconfigboard.h" + "description": "F429I-DISCO with STM32F429", + "port": "stm32", + "board": "STM32F429DISC", + "board_name": "F429I-DISCO", + "mcu_name": "STM32F429", + "path": "ports/stm32/boards/STM32F429DISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "IDK_BLYST_NANO", - "board_name": "IDK-BLYST-NANO", - "mcu_name": "NRF52832", - "description": "IDK-BLYST-NANO with NRF52832", - "path": "repos/micropython/ports/nrf/boards/IDK_BLYST_NANO/mpconfigboard.h" + "description": "F4DISC with STM32F407", + "port": "stm32", + "board": "STM32F4DISC", + "board_name": "F4DISC", + "mcu_name": "STM32F407", + "path": "ports/stm32/boards/STM32F4DISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "MICROBIT", - "board_name": "micro:bit", - "mcu_name": "NRF51822", - "description": "micro:bit with NRF51822", - "path": "repos/micropython/ports/nrf/boards/MICROBIT/mpconfigboard.h" + "description": "F769DISC with STM32F769", + "port": "stm32", + "board": "STM32F769DISC", + "board_name": "F769DISC", + "mcu_name": "STM32F769", + "path": "ports/stm32/boards/STM32F769DISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "NRF52840_MDK_USB_DONGLE", - "board_name": "MDK-USB-DONGLE", - "mcu_name": "NRF52840", - "description": "MDK-USB-DONGLE with NRF52840", - "path": "repos/micropython/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/mpconfigboard.h" + "description": "F7DISC with STM32F746", + "port": "stm32", + "board": "STM32F7DISC", + "board_name": "F7DISC", + "mcu_name": "STM32F746", + "path": "ports/stm32/boards/STM32F7DISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PARTICLE_XENON", - "board_name": "XENON", - "mcu_name": "NRF52840", - "description": "XENON with NRF52840", - "path": "repos/micropython/ports/nrf/boards/PARTICLE_XENON/mpconfigboard.h" + "description": "Feather M0 Express with SAMD21G18A", + "port": "samd", + "board": "ADAFRUIT_FEATHER_M0_EXPRESS", + "board_name": "Feather M0 Express", + "mcu_name": "SAMD21G18A", + "path": "ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10000", - "board_name": "PCA10000", - "mcu_name": "NRF51822", - "description": "PCA10000 with NRF51822", - "path": "repos/micropython/ports/nrf/boards/PCA10000/mpconfigboard.h" + "description": "Feather M4 Express with SAMD51J19A", + "port": "samd", + "board": "ADAFRUIT_FEATHER_M4_EXPRESS", + "board_name": "Feather M4 Express", + "mcu_name": "SAMD51J19A", + "path": "ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10001", - "board_name": "PCA10001", - "mcu_name": "NRF51822", - "description": "PCA10001 with NRF51822", - "path": "repos/micropython/ports/nrf/boards/PCA10001/mpconfigboard.h" + "description": "FeatherS2 Neo with ESP32-S2FN4R2", + "port": "esp32", + "board": "UM_FEATHERS2NEO", + "board_name": "FeatherS2 Neo", + "mcu_name": "ESP32-S2FN4R2", + "path": "ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10028", - "board_name": "PCA10028", - "mcu_name": "NRF51822", - "description": "PCA10028 with NRF51822", - "path": "repos/micropython/ports/nrf/boards/PCA10028/mpconfigboard.h" + "description": "FeatherS2 with ESP32-S2", + "port": "esp32", + "board": "UM_FEATHERS2", + "board_name": "FeatherS2", + "mcu_name": "ESP32-S2", + "path": "ports/esp32/boards/UM_FEATHERS2/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10031", - "board_name": "PCA10031", - "mcu_name": "NRF51822", - "description": "PCA10031 with NRF51822", - "path": "repos/micropython/ports/nrf/boards/PCA10031/mpconfigboard.h" + "description": "FeatherS3 with ESP32-S3", + "port": "esp32", + "board": "UM_FEATHERS3", + "board_name": "FeatherS3", + "mcu_name": "ESP32-S3", + "path": "ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10040", - "board_name": "PCA10040", - "mcu_name": "NRF52832", - "description": "PCA10040 with NRF52832", - "path": "repos/micropython/ports/nrf/boards/PCA10040/mpconfigboard.h" + "description": "GARATRONIC_PYBSTICK26_RP2040", + "port": "rp2", + "board": "GARATRONIC_PYBSTICK26_RP2040", + "board_name": "GARATRONIC_PYBSTICK26_RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10056", - "board_name": "PCA10056", - "mcu_name": "NRF52840", - "description": "PCA10056 with NRF52840", - "path": "repos/micropython/ports/nrf/boards/PCA10056/mpconfigboard.h" + "description": "Generic ESP32 module with ESP32", + "port": "esp32", + "board": "ESP32_GENERIC", + "board_name": "Generic ESP32 module", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/ESP32_GENERIC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10059", - "board_name": "PCA10059", - "mcu_name": "NRF52840", - "description": "PCA10059 with NRF52840", - "path": "repos/micropython/ports/nrf/boards/PCA10059/mpconfigboard.h" + "description": "Generic ESP32 module with OTA", + "port": "esp32", + "board": "ESP32_GENERIC", + "board_name": "-", + "mcu_name": "-", + "path": "ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "PCA10090", - "board_name": "PCA10090", - "mcu_name": "NRF9160", - "description": "PCA10090 with NRF9160", - "path": "repos/micropython/ports/nrf/boards/PCA10090/mpconfigboard.h" + "description": "Generic ESP32 module with SPIRAM", + "port": "esp32", + "board": "ESP32_GENERIC", + "board_name": "-", + "mcu_name": "-", + "path": "ports/esp32/boards/ESP32_GENERIC/mpconfigboard.cmake", + "version": "v1.22.0" }, { - "port": "nrf", - "board": "SEEED_XIAO_NRF52", - "board_name": "XIAO nRF52840 Sense", - "mcu_name": "NRF52840", - "description": "XIAO nRF52840 Sense with NRF52840", - "path": "repos/micropython/ports/nrf/boards/SEEED_XIAO_NRF52/mpconfigboard.h" + "description": "Generic ESP32-D2WD module with ESP32-D2WD", + "port": "esp32", + "board": "GENERIC_D2WD", + "board_name": "Generic ESP32-D2WD module", + "mcu_name": "ESP32-D2WD", + "path": "ports/esp32/boards/GENERIC_D2WD/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "nrf", - "board": "WT51822_S4AT", - "board_name": "WT51822-S4AT", - "mcu_name": "NRF51822", - "description": "WT51822-S4AT with NRF51822", - "path": "repos/micropython/ports/nrf/boards/WT51822_S4AT/mpconfigboard.h" + "description": "Generic ESP32S2 module with ESP32S2", + "port": "esp32", + "board": "ESP32_GENERIC_S2", + "board_name": "Generic ESP32S2 module", + "mcu_name": "ESP32S2", + "path": "ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "renesas-ra", - "board": "ARDUINO_PORTENTA_C33", - "board_name": "PORTENTA C33", - "mcu_name": "RA6M5", - "description": "PORTENTA C33 with RA6M5", - "path": "repos/micropython/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h" + "description": "Generic ESP32S3 module with ESP32S3", + "port": "esp32", + "board": "ESP32_GENERIC_S3", + "board_name": "Generic ESP32S3 module", + "mcu_name": "ESP32S3", + "path": "ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "renesas-ra", - "board": "EK_RA4M1", - "board_name": "EK-RA4M1", - "mcu_name": "RA4M1", - "description": "EK-RA4M1 with RA4M1", - "path": "repos/micropython/ports/renesas-ra/boards/EK_RA4M1/mpconfigboard.h" + "description": "Generic ESP32S3 module with Octal-SPIRAM", + "port": "esp32", + "board": "ESP32_GENERIC_S3", + "board_name": "-", + "mcu_name": "-", + "path": "ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake", + "version": "v1.22.0" }, { - "port": "renesas-ra", - "board": "EK_RA4W1", - "board_name": "EK-RA4W1", - "mcu_name": "RA4W1", - "description": "EK-RA4W1 with RA4W1", - "path": "repos/micropython/ports/renesas-ra/boards/EK_RA4W1/mpconfigboard.h" + "description": "GIGA with STM32H747", + "port": "stm32", + "board": "ARDUINO_GIGA", + "board_name": "GIGA", + "mcu_name": "STM32H747", + "path": "ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h", + "version": "v1.21.0" }, { - "port": "renesas-ra", - "board": "EK_RA6M1", - "board_name": "EK-RA6M1", - "mcu_name": "RA6M1", - "description": "EK-RA6M1 with RA6M1", - "path": "repos/micropython/ports/renesas-ra/boards/EK_RA6M1/mpconfigboard.h" + "description": "HydraBus1.0 with STM32F4", + "port": "stm32", + "board": "HYDRABUS", + "board_name": "HydraBus1.0", + "mcu_name": "STM32F4", + "path": "ports/stm32/boards/HYDRABUS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "renesas-ra", - "board": "EK_RA6M2", - "board_name": "EK-RA6M2", - "mcu_name": "RA6M2", - "description": "EK-RA6M2 with RA6M2", - "path": "repos/micropython/ports/renesas-ra/boards/EK_RA6M2/mpconfigboard.h" + "description": "i.MX RT1010 EVK with MIMXRT1011DAE5A", + "port": "mimxrt", + "board": "MIMXRT1010_EVK", + "board_name": "i.MX RT1010 EVK", + "mcu_name": "MIMXRT1011DAE5A", + "path": "ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "renesas-ra", - "board": "RA4M1_CLICKER", - "board_name": "RA4M1 CLICKER", - "mcu_name": "RA4M1", - "description": "RA4M1 CLICKER with RA4M1", - "path": "repos/micropython/ports/renesas-ra/boards/RA4M1_CLICKER/mpconfigboard.h" + "description": "i.MX RT1015 EVK with MIMXRT1015DAF5A", + "port": "mimxrt", + "board": "MIMXRT1015_EVK", + "board_name": "i.MX RT1015 EVK", + "mcu_name": "MIMXRT1015DAF5A", + "path": "ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "renesas-ra", - "board": "VK_RA6M5", - "board_name": "VK-RA6M5", - "mcu_name": "RA6M5", - "description": "VK-RA6M5 with RA6M5", - "path": "repos/micropython/ports/renesas-ra/boards/VK_RA6M5/mpconfigboard.h" + "description": "i.MX RT1020 EVK with MIMXRT1021DAG5A", + "port": "mimxrt", + "board": "MIMXRT1020_EVK", + "board_name": "i.MX RT1020 EVK", + "mcu_name": "MIMXRT1021DAG5A", + "path": "ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "ADAFRUIT_FEATHER_RP2040", - "board_name": "Adafruit Feather RP2040", - "mcu_name": "-", - "description": "Adafruit Feather RP2040", - "path": "repos/micropython/ports/rp2/boards/ADAFRUIT_FEATHER_RP2040/mpconfigboard.h" + "description": "i.MX RT1050 EVK with MIMXRT1052DVL6B", + "port": "mimxrt", + "board": "MIMXRT1050_EVK", + "board_name": "i.MX RT1050 EVK", + "mcu_name": "MIMXRT1052DVL6B", + "path": "ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h", + "version": "v1.17" }, { - "port": "rp2", - "board": "ADAFRUIT_ITSYBITSY_RP2040", - "board_name": "Adafruit ItsyBitsy RP2040", - "mcu_name": "-", - "description": "Adafruit ItsyBitsy RP2040", - "path": "repos/micropython/ports/rp2/boards/ADAFRUIT_ITSYBITSY_RP2040/mpconfigboard.h" + "description": "i.MX RT1050 EVKB with MIMXRT1052DVL6B", + "port": "mimxrt", + "board": "MIMXRT1050_EVKB", + "board_name": "i.MX RT1050 EVKB", + "mcu_name": "MIMXRT1052DVL6B", + "path": "ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h", + "version": "v1.17" }, { - "port": "rp2", - "board": "ADAFRUIT_QTPY_RP2040", - "board_name": "Adafruit QT Py RP2040", - "mcu_name": "-", - "description": "Adafruit QT Py RP2040", - "path": "repos/micropython/ports/rp2/boards/ADAFRUIT_QTPY_RP2040/mpconfigboard.h" + "description": "i.MX RT1050 EVKB-A1 with MIMXRT1052DVL6B", + "port": "mimxrt", + "board": "MIMXRT1050_EVK", + "board_name": "i.MX RT1050 EVKB-A1", + "mcu_name": "MIMXRT1052DVL6B", + "path": "ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "ARDUINO_NANO_RP2040_CONNECT", - "board_name": "Arduino Nano RP2040 Connect", - "mcu_name": "-", - "description": "Arduino Nano RP2040 Connect", - "path": "repos/micropython/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h" + "description": "i.MX RT1060 EVK with MIMXRT1062DVJ6A", + "port": "mimxrt", + "board": "MIMXRT1060_EVK", + "board_name": "i.MX RT1060 EVK", + "mcu_name": "MIMXRT1062DVJ6A", + "path": "ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "GARATRONIC_PYBSTICK26_RP2040", - "board_name": "GARATRONIC_PYBSTICK26_RP2040", - "mcu_name": "-", - "description": "GARATRONIC_PYBSTICK26_RP2040", - "path": "repos/micropython/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/mpconfigboard.h" + "description": "i.MX RT1064 EVK with MIMXRT1064DVL6A", + "port": "mimxrt", + "board": "MIMXRT1064_EVK", + "board_name": "i.MX RT1064 EVK", + "mcu_name": "MIMXRT1064DVL6A", + "path": "ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "NULLBITS_BIT_C_PRO", - "board_name": "nullbits Bit-C PRO", - "mcu_name": "-", - "description": "nullbits Bit-C PRO", - "path": "repos/micropython/ports/rp2/boards/NULLBITS_BIT_C_PRO/mpconfigboard.h" + "description": "i.MX RT1170 EVK with MIMXRT1176DVMAA", + "port": "mimxrt", + "board": "MIMXRT1170_EVK", + "board_name": "i.MX RT1170 EVK", + "mcu_name": "MIMXRT1176DVMAA", + "path": "ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "PIMORONI_PICOLIPO_16MB", - "board_name": "Pimoroni Pico LiPo 16MB", - "mcu_name": "-", - "description": "Pimoroni Pico LiPo 16MB", - "path": "repos/micropython/ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h" + "description": "IBK-BLYST-NANO with NRF52832", + "port": "nrf", + "board": "IBK_BLYST_NANO", + "board_name": "IBK-BLYST-NANO", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/IBK_BLYST_NANO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "PIMORONI_PICOLIPO_4MB", - "board_name": "Pimoroni Pico LiPo 4MB", - "mcu_name": "-", - "description": "Pimoroni Pico LiPo 4MB", - "path": "repos/micropython/ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h" + "description": "IDK-BLYST-NANO with NRF52832", + "port": "nrf", + "board": "IDK_BLYST_NANO", + "board_name": "IDK-BLYST-NANO", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/IDK_BLYST_NANO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "PIMORONI_TINY2040", - "board_name": "Pimoroni Tiny 2040", - "mcu_name": "-", - "description": "Pimoroni Tiny 2040", - "path": "repos/micropython/ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h" + "description": "ItsyBitsy M0 Express with SAMD21G18A", + "port": "samd", + "board": "ADAFRUIT_ITSYBITSY_M0_EXPRESS", + "board_name": "ItsyBitsy M0 Express", + "mcu_name": "SAMD21G18A", + "path": "ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "RPI_PICO", - "board_name": "Raspberry Pi Pico", - "mcu_name": "-", - "description": "Raspberry Pi Pico", - "path": "repos/micropython/ports/rp2/boards/RPI_PICO/mpconfigboard.h" + "description": "ItsyBitsy M4 Express with SAMD51G19A", + "port": "samd", + "board": "ADAFRUIT_ITSYBITSY_M4_EXPRESS", + "board_name": "ItsyBitsy M4 Express", + "mcu_name": "SAMD51G19A", + "path": "ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "RPI_PICO_W", - "board_name": "Raspberry Pi Pico W", - "mcu_name": "-", - "description": "Raspberry Pi Pico W", - "path": "repos/micropython/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h" + "description": "L476-DISCO with STM32L476", + "port": "stm32", + "board": "STM32L476DISC", + "board_name": "L476-DISCO", + "mcu_name": "STM32L476", + "path": "ports/stm32/boards/STM32L476DISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "SPARKFUN_PROMICRO", - "board_name": "SparkFun Pro Micro RP2040", - "mcu_name": "-", - "description": "SparkFun Pro Micro RP2040", - "path": "repos/micropython/ports/rp2/boards/SPARKFUN_PROMICRO/mpconfigboard.h" + "description": "L496G-DISCO with STM32L496", + "port": "stm32", + "board": "STM32L496GDISC", + "board_name": "L496G-DISCO", + "mcu_name": "STM32L496", + "path": "ports/stm32/boards/STM32L496GDISC/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "SPARKFUN_THINGPLUS", - "board_name": "SparkFun Thing Plus RP2040", - "mcu_name": "-", - "description": "SparkFun Thing Plus RP2040", - "path": "repos/micropython/ports/rp2/boards/SPARKFUN_THINGPLUS/mpconfigboard.h" + "description": "LaunchPad with CC3200", + "port": "cc3200", + "board": "LAUNCHXL", + "board_name": "LaunchPad", + "mcu_name": "CC3200", + "path": "ports/cc3200/boards/LAUNCHXL/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "W5100S_EVB_PICO", - "board_name": "W5100S-EVB-Pico", - "mcu_name": "-", - "description": "W5100S-EVB-Pico", - "path": "repos/micropython/ports/rp2/boards/W5100S_EVB_PICO/mpconfigboard.h" + "description": "LEGO Technic Hub No.6 with STM32F413", + "port": "stm32", + "board": "LEGO_HUB_NO6", + "board_name": "LEGO Technic Hub No.6", + "mcu_name": "STM32F413", + "path": "ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "LEGO Technic Hub No.7 with STM32F413", + "port": "stm32", + "board": "LEGO_HUB_NO7", + "board_name": "LEGO Technic Hub No.7", + "mcu_name": "STM32F413", + "path": "ports/stm32/boards/LEGO_HUB_NO7/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "LILYGO TTGO LoRa32 with ESP32", + "port": "esp32", + "board": "LILYGO_TTGO_LORA32", + "board_name": "LILYGO TTGO LoRa32", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/LILYGO_TTGO_LORA32/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "LIMIFROG with STM32L476", + "port": "stm32", + "board": "LIMIFROG", + "board_name": "LIMIFROG", + "mcu_name": "STM32L476", + "path": "ports/stm32/boards/LIMIFROG/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "LOLIN_C3_MINI with ESP32-C3FH4", + "port": "esp32", + "board": "LOLIN_C3_MINI", + "board_name": "LOLIN_C3_MINI", + "mcu_name": "ESP32-C3FH4", + "path": "ports/esp32/boards/LOLIN_C3_MINI/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "LOLIN_S2_MINI with ESP32-S2FN4R2", + "port": "esp32", + "board": "LOLIN_S2_MINI", + "board_name": "LOLIN_S2_MINI", + "mcu_name": "ESP32-S2FN4R2", + "path": "ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "LOLIN_S2_PICO with ESP32-S2FN4R2", + "port": "esp32", + "board": "LOLIN_S2_PICO", + "board_name": "LOLIN_S2_PICO", + "mcu_name": "ESP32-S2FN4R2", + "path": "ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "M5Stack ATOM with ESP32-PICO-D4", + "port": "esp32", + "board": "M5STACK_ATOM", + "board_name": "M5Stack ATOM", + "mcu_name": "ESP32-PICO-D4", + "path": "ports/esp32/boards/M5STACK_ATOM/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "MDK-USB-DONGLE with NRF52840", + "port": "nrf", + "board": "NRF52840_MDK_USB_DONGLE", + "board_name": "MDK-USB-DONGLE", + "mcu_name": "NRF52840", + "path": "ports/nrf/boards/NRF52840_MDK_USB_DONGLE/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "Metro M4 Express Airlift with SAMD51J19A", + "port": "samd", + "board": "ADAFRUIT_METRO_M4_EXPRESS", + "board_name": "Metro M4 Express Airlift", + "mcu_name": "SAMD51J19A", + "path": "ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "micro:bit with NRF51822", + "port": "nrf", + "board": "MICROBIT", + "board_name": "micro:bit", + "mcu_name": "NRF51822", + "path": "ports/nrf/boards/MICROBIT/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "MikroE Quail with STM32F427VI", + "port": "stm32", + "board": "MIKROE_QUAIL", + "board_name": "MikroE Quail", + "mcu_name": "STM32F427VI", + "path": "ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "MIKROE_CLICKER2_STM32 with STM32F407", + "port": "stm32", + "board": "MIKROE_CLICKER2_STM32", + "board_name": "MIKROE_CLICKER2_STM32", + "mcu_name": "STM32F407", + "path": "ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "Mini SAM M4 with SAMD51G19A", + "port": "samd", + "board": "MINISAM_M4", + "board_name": "Mini SAM M4", + "mcu_name": "SAMD51G19A", + "path": "ports/samd/boards/MINISAM_M4/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "NADHAT_PYBF405 with STM32F405RG", + "port": "stm32", + "board": "GARATRONIC_NADHAT_F405", + "board_name": "NADHAT_PYBF405", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/GARATRONIC_NADHAT_F405/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "NanoS3 with ESP32-S3-FN8", + "port": "esp32", + "board": "UM_NANOS3", + "board_name": "NanoS3", + "mcu_name": "ESP32-S3-FN8", + "path": "ports/esp32/boards/UM_NANOS3/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "NetduinoPlus2 with STM32F405RG", + "port": "stm32", + "board": "NETDUINO_PLUS_2", + "board_name": "NetduinoPlus2", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/NETDUINO_PLUS_2/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "NICLAVISION with STM32H747", + "port": "stm32", + "board": "ARDUINO_NICLA_VISION", + "board_name": "NICLAVISION", + "mcu_name": "STM32H747", + "path": "ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h", + "version": "v1.21.0" + }, + { + "description": "NUCLEO-F091RC with STM32F091RCT6", + "port": "stm32", + "board": "NUCLEO_F091RC", + "board_name": "NUCLEO-F091RC", + "mcu_name": "STM32F091RCT6", + "path": "ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "NUCLEO-F401RE with STM32F401xE", + "port": "stm32", + "board": "NUCLEO_F401RE", + "board_name": "NUCLEO-F401RE", + "mcu_name": "STM32F401xE", + "path": "ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "NUCLEO-F411RE with STM32F411xE", + "port": "stm32", + "board": "NUCLEO_F411RE", + "board_name": "NUCLEO-F411RE", + "mcu_name": "STM32F411xE", + "path": "ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "W5500_EVB_PICO", - "board_name": "W5500-EVB-Pico", - "mcu_name": "-", - "description": "W5500-EVB-Pico", - "path": "repos/micropython/ports/rp2/boards/W5500_EVB_PICO/mpconfigboard.h" + "description": "NUCLEO-F412ZG with STM32F412Zx", + "port": "stm32", + "board": "NUCLEO_F412ZG", + "board_name": "NUCLEO-F412ZG", + "mcu_name": "STM32F412Zx", + "path": "ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "rp2", - "board": "WEACTSTUDIO", - "board_name": "WeAct Studio RP2040", - "mcu_name": "-", - "description": "WeAct Studio RP2040", - "path": "repos/micropython/ports/rp2/boards/WEACTSTUDIO/mpconfigboard.h" + "description": "NUCLEO-F413ZH with STM32F413", + "port": "stm32", + "board": "NUCLEO_F413ZH", + "board_name": "NUCLEO-F413ZH", + "mcu_name": "STM32F413", + "path": "ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "ADAFRUIT_FEATHER_M0_EXPRESS", - "board_name": "Feather M0 Express", - "mcu_name": "SAMD21G18A", - "description": "Feather M0 Express with SAMD21G18A", - "path": "repos/micropython/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h" + "description": "NUCLEO-F429ZI with STM32F429", + "port": "stm32", + "board": "NUCLEO_F429ZI", + "board_name": "NUCLEO-F429ZI", + "mcu_name": "STM32F429", + "path": "ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "ADAFRUIT_FEATHER_M4_EXPRESS", - "board_name": "Feather M4 Express", - "mcu_name": "SAMD51J19A", - "description": "Feather M4 Express with SAMD51J19A", - "path": "repos/micropython/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.h" + "description": "NUCLEO-F439ZI with STM32F439ZIT6", + "port": "stm32", + "board": "NUCLEO_F439ZI", + "board_name": "NUCLEO-F439ZI", + "mcu_name": "STM32F439ZIT6", + "path": "ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "ADAFRUIT_ITSYBITSY_M0_EXPRESS", - "board_name": "ItsyBitsy M0 Express", - "mcu_name": "SAMD21G18A", - "description": "ItsyBitsy M0 Express with SAMD21G18A", - "path": "repos/micropython/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.h" + "description": "NUCLEO-F446RE with STM32F446xx", + "port": "stm32", + "board": "NUCLEO_F446RE", + "board_name": "NUCLEO-F446RE", + "mcu_name": "STM32F446xx", + "path": "ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "ADAFRUIT_ITSYBITSY_M4_EXPRESS", - "board_name": "ItsyBitsy M4 Express", - "mcu_name": "SAMD51G19A", - "description": "ItsyBitsy M4 Express with SAMD51G19A", - "path": "repos/micropython/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.h" + "description": "NUCLEO-F722ZE with STM32F722", + "port": "stm32", + "board": "NUCLEO_F722ZE", + "board_name": "NUCLEO-F722ZE", + "mcu_name": "STM32F722", + "path": "ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "ADAFRUIT_METRO_M4_EXPRESS", - "board_name": "Metro M4 Express Airlift", - "mcu_name": "SAMD51J19A", - "description": "Metro M4 Express Airlift with SAMD51J19A", - "path": "repos/micropython/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.h" + "description": "NUCLEO-F746ZG with STM32F746", + "port": "stm32", + "board": "NUCLEO_F746ZG", + "board_name": "NUCLEO-F746ZG", + "mcu_name": "STM32F746", + "path": "ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "ADAFRUIT_TRINKET_M0", - "board_name": "Trinket M0", - "mcu_name": "SAMD21E18A", - "description": "Trinket M0 with SAMD21E18A", - "path": "repos/micropython/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h" + "description": "NUCLEO-F756ZG with STM32F756", + "port": "stm32", + "board": "NUCLEO_F756ZG", + "board_name": "NUCLEO-F756ZG", + "mcu_name": "STM32F756", + "path": "ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "MINISAM_M4", - "board_name": "Mini SAM M4", - "mcu_name": "SAMD51G19A", - "description": "Mini SAM M4 with SAMD51G19A", - "path": "repos/micropython/ports/samd/boards/MINISAM_M4/mpconfigboard.h" + "description": "NUCLEO-F767ZI with STM32F767", + "port": "stm32", + "board": "NUCLEO_F767ZI", + "board_name": "NUCLEO-F767ZI", + "mcu_name": "STM32F767", + "path": "ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "SAMD21_XPLAINED_PRO", - "board_name": "SAMD21-XPLAINED-PRO", - "mcu_name": "SAMD21J18A", - "description": "SAMD21-XPLAINED-PRO with SAMD21J18A", - "path": "repos/micropython/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h" + "description": "NUCLEO-G0B1RE with STM32G0B1xE", + "port": "stm32", + "board": "NUCLEO_G0B1RE", + "board_name": "NUCLEO-G0B1RE", + "mcu_name": "STM32G0B1xE", + "path": "ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "SEEED_WIO_TERMINAL", - "board_name": "Wio Terminal D51R", - "mcu_name": "SAMD51P19A", - "description": "Wio Terminal D51R with SAMD51P19A", - "path": "repos/micropython/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h" + "description": "NUCLEO-L073RZ with STM32L073RZT6", + "port": "stm32", + "board": "NUCLEO_L073RZ", + "board_name": "NUCLEO-L073RZ", + "mcu_name": "STM32L073RZT6", + "path": "ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "SEEED_XIAO_SAMD21", - "board_name": "Seeed Xiao", - "mcu_name": "SAMD21G18A", - "description": "Seeed Xiao with SAMD21G18A", - "path": "repos/micropython/ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h" + "description": "NUCLEO-L152RE with STM32L152xE", + "port": "stm32", + "board": "NUCLEO_L152RE", + "board_name": "NUCLEO-L152RE", + "mcu_name": "STM32L152xE", + "path": "ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "samd", - "board": "SPARKFUN_SAMD51_THING_PLUS", - "board_name": "Sparkfun SAMD51 Thing Plus", - "mcu_name": "SAMD51J20A", - "description": "Sparkfun SAMD51 Thing Plus with SAMD51J20A", - "path": "repos/micropython/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h" + "description": "NUCLEO-L432KC with STM32L432KC", + "port": "stm32", + "board": "NUCLEO_L432KC", + "board_name": "NUCLEO-L432KC", + "mcu_name": "STM32L432KC", + "path": "ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO-L452RE with STM32L452RE", "port": "stm32", - "board": "ADAFRUIT_F405_EXPRESS", - "board_name": "Adafruit Feather STM32F405", - "mcu_name": "STM32F405RG", - "description": "Adafruit Feather STM32F405 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h" + "board": "NUCLEO_L452RE", + "board_name": "NUCLEO-L452RE", + "mcu_name": "STM32L452RE", + "path": "ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO-L476RG with STM32L476RG", "port": "stm32", - "board": "ARDUINO_GIGA", - "board_name": "GIGA", - "mcu_name": "STM32H747", - "description": "GIGA with STM32H747", - "path": "repos/micropython/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h" + "board": "NUCLEO_L476RG", + "board_name": "NUCLEO-L476RG", + "mcu_name": "STM32L476RG", + "path": "ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO-L4A6ZG with STM32L4A6ZG", "port": "stm32", - "board": "ARDUINO_NICLA_VISION", - "board_name": "NICLAVISION", - "mcu_name": "STM32H747", - "description": "NICLAVISION with STM32H747", - "path": "repos/micropython/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h" + "board": "NUCLEO_L4A6ZG", + "board_name": "NUCLEO-L4A6ZG", + "mcu_name": "STM32L4A6ZG", + "path": "ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO-WB55 with STM32WB55RGV6", "port": "stm32", - "board": "ARDUINO_PORTENTA_H7", - "board_name": "PORTENTA", - "mcu_name": "STM32H747", - "description": "PORTENTA with STM32H747", - "path": "repos/micropython/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h" + "board": "NUCLEO_WB55", + "board_name": "NUCLEO-WB55", + "mcu_name": "STM32WB55RGV6", + "path": "ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO-WL55 with STM32WL55JCI7", "port": "stm32", - "board": "B_L072Z_LRWAN1", - "board_name": "B-L072Z-LRWAN1", - "mcu_name": "STM32L072CZ", - "description": "B-L072Z-LRWAN1 with STM32L072CZ", - "path": "repos/micropython/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h" + "board": "NUCLEO_WL55", + "board_name": "NUCLEO-WL55", + "mcu_name": "STM32WL55JCI7", + "path": "ports/stm32/boards/NUCLEO_WL55/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO_G474RE with STM32G474", "port": "stm32", - "board": "B_L475E_IOT01A", - "board_name": "B-L475E-IOT01A", - "mcu_name": "STM32L475", - "description": "B-L475E-IOT01A with STM32L475", - "path": "repos/micropython/ports/stm32/boards/B_L475E_IOT01A/mpconfigboard.h" + "board": "NUCLEO_G474RE", + "board_name": "NUCLEO_G474RE", + "mcu_name": "STM32G474", + "path": "ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO_H563ZI with STM32H563ZI", "port": "stm32", - "board": "CERB40", - "board_name": "Cerb40", - "mcu_name": "STM32F405RG", - "description": "Cerb40 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/CERB40/mpconfigboard.h" + "board": "NUCLEO_H563ZI", + "board_name": "NUCLEO_H563ZI", + "mcu_name": "STM32H563ZI", + "path": "ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO_H723ZG with STM32H723ZGT6", "port": "stm32", - "board": "ESPRUINO_PICO", - "board_name": "Espruino Pico", - "mcu_name": "STM32F401CD", - "description": "Espruino Pico with STM32F401CD", - "path": "repos/micropython/ports/stm32/boards/ESPRUINO_PICO/mpconfigboard.h" + "board": "NUCLEO_H723ZG", + "board_name": "NUCLEO_H723ZG", + "mcu_name": "STM32H723ZGT6", + "path": "ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO_H743ZI with STM32H743", "port": "stm32", - "board": "GARATRONIC_NADHAT_F405", - "board_name": "NADHAT_PYBF405", - "mcu_name": "STM32F405RG", - "description": "NADHAT_PYBF405 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/GARATRONIC_NADHAT_F405/mpconfigboard.h" + "board": "NUCLEO_H743ZI", + "board_name": "NUCLEO_H743ZI", + "mcu_name": "STM32H743", + "path": "ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "NUCLEO_H743ZI2", "port": "stm32", - "board": "GARATRONIC_PYBSTICK26_F411", - "board_name": "PYBSTICK26_STD", - "mcu_name": "STM32F411RE", - "description": "PYBSTICK26_STD with STM32F411RE", - "path": "repos/micropython/ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/mpconfigboard.h" + "board": "NUCLEO_H743ZI2", + "board_name": "NUCLEO_H743ZI2", + "mcu_name": "-", + "path": "ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "HYDRABUS", - "board_name": "HydraBus1.0", - "mcu_name": "STM32F4", - "description": "HydraBus1.0 with STM32F4", - "path": "repos/micropython/ports/stm32/boards/HYDRABUS/mpconfigboard.h" + "description": "nullbits Bit-C PRO", + "port": "rp2", + "board": "NULLBITS_BIT_C_PRO", + "board_name": "nullbits Bit-C PRO", + "mcu_name": "-", + "path": "ports/rp2/boards/NULLBITS_BIT_C_PRO/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "Olimex ESP32 ETH with ESP32", + "port": "esp32", + "board": "OLIMEX_ESP32_POE", + "board_name": "Olimex ESP32 ETH", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/OLIMEX_ESP32_POE/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "OLIMEX STM32-E407 with STM32F407", "port": "stm32", - "board": "LEGO_HUB_NO6", - "board_name": "LEGO Technic Hub No.6", - "mcu_name": "STM32F413", - "description": "LEGO Technic Hub No.6 with STM32F413", - "path": "repos/micropython/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h" + "board": "OLIMEX_E407", + "board_name": "OLIMEX STM32-E407", + "mcu_name": "STM32F407", + "path": "ports/stm32/boards/OLIMEX_E407/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "OLIMEX STM32-H407 with STM32F407", "port": "stm32", - "board": "LEGO_HUB_NO7", - "board_name": "LEGO Technic Hub No.7", - "mcu_name": "STM32F413", - "description": "LEGO Technic Hub No.7 with STM32F413", - "path": "repos/micropython/ports/stm32/boards/LEGO_HUB_NO7/mpconfigboard.h" + "board": "OLIMEX_H407", + "board_name": "OLIMEX STM32-H407", + "mcu_name": "STM32F407", + "path": "ports/stm32/boards/OLIMEX_H407/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "PCA10000 with NRF51822", + "port": "nrf", + "board": "PCA10000", + "board_name": "PCA10000", + "mcu_name": "NRF51822", + "path": "ports/nrf/boards/PCA10000/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "PCA10001 with NRF51822", + "port": "nrf", + "board": "PCA10001", + "board_name": "PCA10001", + "mcu_name": "NRF51822", + "path": "ports/nrf/boards/PCA10001/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "LIMIFROG", - "board_name": "LIMIFROG", - "mcu_name": "STM32L476", - "description": "LIMIFROG with STM32L476", - "path": "repos/micropython/ports/stm32/boards/LIMIFROG/mpconfigboard.h" + "description": "PCA10028 with NRF51822", + "port": "nrf", + "board": "PCA10028", + "board_name": "PCA10028", + "mcu_name": "NRF51822", + "path": "ports/nrf/boards/PCA10028/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "MIKROE_CLICKER2_STM32", - "board_name": "MIKROE_CLICKER2_STM32", - "mcu_name": "STM32F407", - "description": "MIKROE_CLICKER2_STM32 with STM32F407", - "path": "repos/micropython/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h" + "description": "PCA10031 with NRF51822", + "port": "nrf", + "board": "PCA10031", + "board_name": "PCA10031", + "mcu_name": "NRF51822", + "path": "ports/nrf/boards/PCA10031/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "MIKROE_QUAIL", - "board_name": "MikroE Quail", - "mcu_name": "STM32F427VI", - "description": "MikroE Quail with STM32F427VI", - "path": "repos/micropython/ports/stm32/boards/MIKROE_QUAIL/mpconfigboard.h" + "description": "PCA10040 with NRF52832", + "port": "nrf", + "board": "PCA10040", + "board_name": "PCA10040", + "mcu_name": "NRF52832", + "path": "ports/nrf/boards/PCA10040/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NETDUINO_PLUS_2", - "board_name": "NetduinoPlus2", - "mcu_name": "STM32F405RG", - "description": "NetduinoPlus2 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/NETDUINO_PLUS_2/mpconfigboard.h" + "description": "PCA10056 with NRF52840", + "port": "nrf", + "board": "PCA10056", + "board_name": "PCA10056", + "mcu_name": "NRF52840", + "path": "ports/nrf/boards/PCA10056/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F091RC", - "board_name": "NUCLEO-F091RC", - "mcu_name": "STM32F091RCT6", - "description": "NUCLEO-F091RC with STM32F091RCT6", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.h" + "description": "PCA10059 with NRF52840", + "port": "nrf", + "board": "PCA10059", + "board_name": "PCA10059", + "mcu_name": "NRF52840", + "path": "ports/nrf/boards/PCA10059/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F401RE", - "board_name": "NUCLEO-F401RE", - "mcu_name": "STM32F401xE", - "description": "NUCLEO-F401RE with STM32F401xE", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F401RE/mpconfigboard.h" + "description": "PCA10090 with NRF9160", + "port": "nrf", + "board": "PCA10090", + "board_name": "PCA10090", + "mcu_name": "NRF9160", + "path": "ports/nrf/boards/PCA10090/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F411RE", - "board_name": "NUCLEO-F411RE", - "mcu_name": "STM32F411xE", - "description": "NUCLEO-F411RE with STM32F411xE", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F411RE/mpconfigboard.h" + "description": "Pimoroni Pico LiPo 16MB", + "port": "rp2", + "board": "PIMORONI_PICOLIPO_16MB", + "board_name": "Pimoroni Pico LiPo 16MB", + "mcu_name": "-", + "path": "ports/rp2/boards/PIMORONI_PICOLIPO_16MB/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F412ZG", - "board_name": "NUCLEO-F412ZG", - "mcu_name": "STM32F412Zx", - "description": "NUCLEO-F412ZG with STM32F412Zx", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F412ZG/mpconfigboard.h" + "description": "Pimoroni Pico LiPo 4MB", + "port": "rp2", + "board": "PIMORONI_PICOLIPO_4MB", + "board_name": "Pimoroni Pico LiPo 4MB", + "mcu_name": "-", + "path": "ports/rp2/boards/PIMORONI_PICOLIPO_4MB/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F413ZH", - "board_name": "NUCLEO-F413ZH", - "mcu_name": "STM32F413", - "description": "NUCLEO-F413ZH with STM32F413", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F413ZH/mpconfigboard.h" + "description": "Pimoroni Tiny 2040", + "port": "rp2", + "board": "PIMORONI_TINY2040", + "board_name": "Pimoroni Tiny 2040", + "mcu_name": "-", + "path": "ports/rp2/boards/PIMORONI_TINY2040/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F429ZI", - "board_name": "NUCLEO-F429ZI", - "mcu_name": "STM32F429", - "description": "NUCLEO-F429ZI with STM32F429", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h" + "description": "Pololu 3pi+ 2040 Robot", + "port": "rp2", + "board": "POLOLU_3PI_2040_ROBOT", + "board_name": "Pololu 3pi+ 2040 Robot", + "mcu_name": "-", + "path": "ports/rp2/boards/POLOLU_3PI_2040_ROBOT/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F439ZI", - "board_name": "NUCLEO-F439ZI", - "mcu_name": "STM32F439ZIT6", - "description": "NUCLEO-F439ZI with STM32F439ZIT6", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F439ZI/mpconfigboard.h" + "description": "Pololu Zumo 2040 Robot", + "port": "rp2", + "board": "POLOLU_ZUMO_2040_ROBOT", + "board_name": "Pololu Zumo 2040 Robot", + "mcu_name": "-", + "path": "ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_F446RE", - "board_name": "NUCLEO-F446RE", - "mcu_name": "STM32F446xx", - "description": "NUCLEO-F446RE with STM32F446xx", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h" + "description": "PORTENTA C33 with RA6M5", + "port": "renesas-ra", + "board": "ARDUINO_PORTENTA_C33", + "board_name": "PORTENTA C33", + "mcu_name": "RA6M5", + "path": "ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h", + "version": "v1.21.0" }, { + "description": "PORTENTA with STM32H747", "port": "stm32", - "board": "NUCLEO_F722ZE", - "board_name": "NUCLEO-F722ZE", - "mcu_name": "STM32F722", - "description": "NUCLEO-F722ZE with STM32F722", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F722ZE/mpconfigboard.h" + "board": "ARDUINO_PORTENTA_H7", + "board_name": "PORTENTA", + "mcu_name": "STM32H747", + "path": "ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h", + "version": "v1.21.0" }, { - "port": "stm32", - "board": "NUCLEO_F746ZG", - "board_name": "NUCLEO-F746ZG", - "mcu_name": "STM32F746", - "description": "NUCLEO-F746ZG with STM32F746", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h" + "description": "ProS3 with ESP32-S3", + "port": "esp32", + "board": "UM_PROS3", + "board_name": "ProS3", + "mcu_name": "ESP32-S3", + "path": "ports/esp32/boards/UM_PROS3/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBD-SF2W with STM32F722IEK", "port": "stm32", - "board": "NUCLEO_F756ZG", - "board_name": "NUCLEO-F756ZG", - "mcu_name": "STM32F756", - "description": "NUCLEO-F756ZG with STM32F756", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F756ZG/mpconfigboard.h" + "board": "PYBD_SF2", + "board_name": "PYBD-SF2W", + "mcu_name": "STM32F722IEK", + "path": "ports/stm32/boards/PYBD_SF2/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBD-SF3W with STM32F733IEK", "port": "stm32", - "board": "NUCLEO_F767ZI", - "board_name": "NUCLEO-F767ZI", - "mcu_name": "STM32F767", - "description": "NUCLEO-F767ZI with STM32F767", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h" + "board": "PYBD_SF3", + "board_name": "PYBD-SF3W", + "mcu_name": "STM32F733IEK", + "path": "ports/stm32/boards/PYBD_SF3/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBD-SF6W with STM32F767IIK", "port": "stm32", - "board": "NUCLEO_G0B1RE", - "board_name": "NUCLEO-G0B1RE", - "mcu_name": "STM32G0B1xE", - "description": "NUCLEO-G0B1RE with STM32G0B1xE", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h" + "board": "PYBD_SF6", + "board_name": "PYBD-SF6W", + "mcu_name": "STM32F767IIK", + "path": "ports/stm32/boards/PYBD_SF6/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBLITEv1.0 with STM32F411RE", "port": "stm32", - "board": "NUCLEO_G474RE", - "board_name": "NUCLEO_G474RE", - "mcu_name": "STM32G474", - "description": "NUCLEO_G474RE with STM32G474", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_G474RE/mpconfigboard.h" + "board": "PYBLITEV10", + "board_name": "PYBLITEv1.0", + "mcu_name": "STM32F411RE", + "path": "ports/stm32/boards/PYBLITEV10/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBSTICK26_STD with STM32F411RE", "port": "stm32", - "board": "NUCLEO_H723ZG", - "board_name": "NUCLEO_H723ZG", - "mcu_name": "STM32H723ZGT6", - "description": "NUCLEO_H723ZG with STM32H723ZGT6", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h" + "board": "GARATRONIC_PYBSTICK26_F411", + "board_name": "PYBSTICK26_STD", + "mcu_name": "STM32F411RE", + "path": "ports/stm32/boards/GARATRONIC_PYBSTICK26_F411/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBv1.0 with STM32F405RG", "port": "stm32", - "board": "NUCLEO_H743ZI", - "board_name": "NUCLEO_H743ZI", - "mcu_name": "STM32H743", - "description": "NUCLEO_H743ZI with STM32H743", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_H743ZI/mpconfigboard.h" + "board": "PYBV10", + "board_name": "PYBv1.0", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/PYBV10/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBv1.1 with STM32F405RG", "port": "stm32", - "board": "NUCLEO_H743ZI2", - "board_name": "NUCLEO_H743ZI2", - "mcu_name": "-", - "description": "NUCLEO_H743ZI2", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_H743ZI2/mpconfigboard.h" + "board": "PYBV11", + "board_name": "PYBv1.1", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/PYBV11/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBv3 with STM32F405RG", "port": "stm32", - "board": "NUCLEO_L073RZ", - "board_name": "NUCLEO-L073RZ", - "mcu_name": "STM32L073RZT6", - "description": "NUCLEO-L073RZ with STM32L073RZT6", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h" + "board": "PYBV3", + "board_name": "PYBv3", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/PYBV3/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "PYBv4 with STM32F405RG", "port": "stm32", - "board": "NUCLEO_L152RE", - "board_name": "NUCLEO-L152RE", - "mcu_name": "STM32L152xE", - "description": "NUCLEO-L152RE with STM32L152xE", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_L152RE/mpconfigboard.h" + "board": "PYBV4", + "board_name": "PYBv4", + "mcu_name": "STM32F405RG", + "path": "ports/stm32/boards/PYBV4/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_L432KC", - "board_name": "NUCLEO-L432KC", - "mcu_name": "STM32L432KC", - "description": "NUCLEO-L432KC with STM32L432KC", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h" + "description": "RA4M1 CLICKER with RA4M1", + "port": "renesas-ra", + "board": "RA4M1_CLICKER", + "board_name": "RA4M1 CLICKER", + "mcu_name": "RA4M1", + "path": "ports/renesas-ra/boards/RA4M1_CLICKER/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "NUCLEO_L452RE", - "board_name": "NUCLEO-L452RE", - "mcu_name": "STM32L452RE", - "description": "NUCLEO-L452RE with STM32L452RE", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_L452RE/mpconfigboard.h" + "description": "RA4M1_CLICKER with RA4M1", + "port": "renesas-ra", + "board": "RA4M1_CLICKER", + "board_name": "RA4M1_CLICKER", + "mcu_name": "RA4M1", + "path": "ports/renesas-ra/boards/RA4M1_CLICKER/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "stm32", - "board": "NUCLEO_L476RG", - "board_name": "NUCLEO-L476RG", - "mcu_name": "STM32L476RG", - "description": "NUCLEO-L476RG with STM32L476RG", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_L476RG/mpconfigboard.h" + "description": "RA4M1_EK with RA4M1", + "port": "renesas-ra", + "board": "RA4M1_EK", + "board_name": "RA4M1_EK", + "mcu_name": "RA4M1", + "path": "ports/renesas-ra/boards/RA4M1_EK/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "stm32", - "board": "NUCLEO_L4A6ZG", - "board_name": "NUCLEO-L4A6ZG", - "mcu_name": "STM32L4A6ZG", - "description": "NUCLEO-L4A6ZG with STM32L4A6ZG", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_L4A6ZG/mpconfigboard.h" + "description": "RA4W1_EK with RA4W1", + "port": "renesas-ra", + "board": "RA4W1_EK", + "board_name": "RA4W1_EK", + "mcu_name": "RA4W1", + "path": "ports/renesas-ra/boards/RA4W1_EK/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "stm32", - "board": "NUCLEO_WB55", - "board_name": "NUCLEO-WB55", - "mcu_name": "STM32WB55RGV6", - "description": "NUCLEO-WB55 with STM32WB55RGV6", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h" + "description": "RA6M1_EK with RA6M1", + "port": "renesas-ra", + "board": "RA6M1_EK", + "board_name": "RA6M1_EK", + "mcu_name": "RA6M1", + "path": "ports/renesas-ra/boards/RA6M1_EK/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "stm32", - "board": "NUCLEO_WL55", - "board_name": "NUCLEO-WL55", - "mcu_name": "STM32WL55JCI7", - "description": "NUCLEO-WL55 with STM32WL55JCI7", - "path": "repos/micropython/ports/stm32/boards/NUCLEO_WL55/mpconfigboard.h" + "description": "RA6M2_EK with RA6M2", + "port": "renesas-ra", + "board": "RA6M2_EK", + "board_name": "RA6M2_EK", + "mcu_name": "RA6M2", + "path": "ports/renesas-ra/boards/RA6M2_EK/mpconfigboard.h", + "version": "v1.20.0" }, { - "port": "stm32", - "board": "OLIMEX_E407", - "board_name": "OLIMEX STM32-E407", - "mcu_name": "STM32F407", - "description": "OLIMEX STM32-E407 with STM32F407", - "path": "repos/micropython/ports/stm32/boards/OLIMEX_E407/mpconfigboard.h" + "description": "Raspberry Pi Pico", + "port": "rp2", + "board": "RPI_PICO", + "board_name": "Raspberry Pi Pico", + "mcu_name": "-", + "path": "ports/rp2/boards/RPI_PICO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "OLIMEX_H407", - "board_name": "OLIMEX STM32-H407", - "mcu_name": "STM32F407", - "description": "OLIMEX STM32-H407 with STM32F407", - "path": "repos/micropython/ports/stm32/boards/OLIMEX_H407/mpconfigboard.h" + "description": "Raspberry Pi Pico W", + "port": "rp2", + "board": "RPI_PICO_W", + "board_name": "Raspberry Pi Pico W", + "mcu_name": "-", + "path": "ports/rp2/boards/RPI_PICO_W/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBD_SF2", - "board_name": "PYBD-SF2W", - "mcu_name": "STM32F722IEK", - "description": "PYBD-SF2W with STM32F722IEK", - "path": "repos/micropython/ports/stm32/boards/PYBD_SF2/mpconfigboard.h" + "description": "RT1010-Py-DevKIT with MIMXRT1011DAE5A", + "port": "mimxrt", + "board": "OLIMEX_RT1010", + "board_name": "RT1010-Py-DevKIT", + "mcu_name": "MIMXRT1011DAE5A", + "path": "ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBD_SF3", - "board_name": "PYBD-SF3W", - "mcu_name": "STM32F733IEK", - "description": "PYBD-SF3W with STM32F733IEK", - "path": "repos/micropython/ports/stm32/boards/PYBD_SF3/mpconfigboard.h" + "description": "SAMD21-XPLAINED-PRO with SAMD21J18A", + "port": "samd", + "board": "SAMD21_XPLAINED_PRO", + "board_name": "SAMD21-XPLAINED-PRO", + "mcu_name": "SAMD21J18A", + "path": "ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBD_SF6", - "board_name": "PYBD-SF6W", - "mcu_name": "STM32F767IIK", - "description": "PYBD-SF6W with STM32F767IIK", - "path": "repos/micropython/ports/stm32/boards/PYBD_SF6/mpconfigboard.h" + "description": "Seeed ARCH MIX with MIMXRT1052DVL5B", + "port": "mimxrt", + "board": "SEEED_ARCH_MIX", + "board_name": "Seeed ARCH MIX", + "mcu_name": "MIMXRT1052DVL5B", + "path": "ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBLITEV10", - "board_name": "PYBLITEv1.0", - "mcu_name": "STM32F411RE", - "description": "PYBLITEv1.0 with STM32F411RE", - "path": "repos/micropython/ports/stm32/boards/PYBLITEV10/mpconfigboard.h" + "description": "Seeed Xiao with SAMD21G18A", + "port": "samd", + "board": "SEEED_XIAO_SAMD21", + "board_name": "Seeed Xiao", + "mcu_name": "SAMD21G18A", + "path": "ports/samd/boards/SEEED_XIAO_SAMD21/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBV10", - "board_name": "PYBv1.0", - "mcu_name": "STM32F405RG", - "description": "PYBv1.0 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/PYBV10/mpconfigboard.h" + "description": "Silicognition RP2040-Shim", + "port": "rp2", + "board": "SIL_RP2040_SHIM", + "board_name": "Silicognition RP2040-Shim", + "mcu_name": "-", + "path": "ports/rp2/boards/SIL_RP2040_SHIM/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBV11", - "board_name": "PYBv1.1", - "mcu_name": "STM32F405RG", - "description": "PYBv1.1 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/PYBV11/mpconfigboard.h" + "description": "Silicognition wESP32 with ESP32", + "port": "esp32", + "board": "SIL_WESP32", + "board_name": "Silicognition wESP32", + "mcu_name": "ESP32", + "path": "ports/esp32/boards/SIL_WESP32/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBV3", - "board_name": "PYBv3", - "mcu_name": "STM32F405RG", - "description": "PYBv3 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/PYBV3/mpconfigboard.h" + "description": "SparkFun Pro Micro RP2040", + "port": "rp2", + "board": "SPARKFUN_PROMICRO", + "board_name": "SparkFun Pro Micro RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/SPARKFUN_PROMICRO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "PYBV4", - "board_name": "PYBv4", - "mcu_name": "STM32F405RG", - "description": "PYBv4 with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/PYBV4/mpconfigboard.h" + "description": "Sparkfun SAMD51 Thing Plus with SAMD51J20A", + "port": "samd", + "board": "SPARKFUN_SAMD51_THING_PLUS", + "board_name": "Sparkfun SAMD51 Thing Plus", + "mcu_name": "SAMD51J20A", + "path": "ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "SparkFun STM32 MicroMod Processor with STM32F405RG", "port": "stm32", "board": "SPARKFUN_MICROMOD_STM32", "board_name": "SparkFun STM32 MicroMod Processor", "mcu_name": "STM32F405RG", - "description": "SparkFun STM32 MicroMod Processor with STM32F405RG", - "path": "repos/micropython/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h" + "path": "ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32F411DISC", - "board_name": "F411DISC", - "mcu_name": "STM32F411", - "description": "F411DISC with STM32F411", - "path": "repos/micropython/ports/stm32/boards/STM32F411DISC/mpconfigboard.h" + "description": "SparkFun Thing Plus RP2040", + "port": "rp2", + "board": "SPARKFUN_THINGPLUS", + "board_name": "SparkFun Thing Plus RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/SPARKFUN_THINGPLUS/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "STM32H573I-DK with STM32H573IIK3Q", "port": "stm32", - "board": "STM32F429DISC", - "board_name": "F429I-DISCO", - "mcu_name": "STM32F429", - "description": "F429I-DISCO with STM32F429", - "path": "repos/micropython/ports/stm32/boards/STM32F429DISC/mpconfigboard.h" + "board": "STM32H573I_DK", + "board_name": "STM32H573I-DK", + "mcu_name": "STM32H573IIK3Q", + "path": "ports/stm32/boards/STM32H573I_DK/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "STM32H7B3I-DK with STM32H7B3LIH6Q", "port": "stm32", - "board": "STM32F439", - "board_name": "CustomPCB", - "mcu_name": "STM32F439", - "description": "CustomPCB with STM32F439", - "path": "repos/micropython/ports/stm32/boards/STM32F439/mpconfigboard.h" + "board": "STM32H7B3I_DK", + "board_name": "STM32H7B3I-DK", + "mcu_name": "STM32H7B3LIH6Q", + "path": "ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32F4DISC", - "board_name": "F4DISC", - "mcu_name": "STM32F407", - "description": "F4DISC with STM32F407", - "path": "repos/micropython/ports/stm32/boards/STM32F4DISC/mpconfigboard.h" + "description": "Teensy 4.0 with MIMXRT1062DVJ6A", + "port": "mimxrt", + "board": "TEENSY40", + "board_name": "Teensy 4.0", + "mcu_name": "MIMXRT1062DVJ6A", + "path": "ports/mimxrt/boards/TEENSY40/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32F769DISC", - "board_name": "F769DISC", - "mcu_name": "STM32F769", - "description": "F769DISC with STM32F769", - "path": "repos/micropython/ports/stm32/boards/STM32F769DISC/mpconfigboard.h" + "description": "Teensy 4.1 with MIMXRT1062DVJ6A", + "port": "mimxrt", + "board": "TEENSY41", + "board_name": "Teensy 4.1", + "mcu_name": "MIMXRT1062DVJ6A", + "path": "ports/mimxrt/boards/TEENSY41/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32F7DISC", - "board_name": "F7DISC", - "mcu_name": "STM32F746", - "description": "F7DISC with STM32F746", - "path": "repos/micropython/ports/stm32/boards/STM32F7DISC/mpconfigboard.h" + "description": "TinyPICO with ESP32-PICO-D4", + "port": "esp32", + "board": "UM_TINYPICO", + "board_name": "TinyPICO", + "mcu_name": "ESP32-PICO-D4", + "path": "ports/esp32/boards/UM_TINYPICO/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32H573I_DK", - "board_name": "STM32H573I-DK", - "mcu_name": "STM32H573IIK3Q", - "description": "STM32H573I-DK with STM32H573IIK3Q", - "path": "repos/micropython/ports/stm32/boards/STM32H573I_DK/mpconfigboard.h" + "description": "TinyS2 with ESP32-S2FN4R2", + "port": "esp32", + "board": "UM_TINYS2", + "board_name": "TinyS2", + "mcu_name": "ESP32-S2FN4R2", + "path": "ports/esp32/boards/UM_TINYS2/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32H7B3I_DK", - "board_name": "STM32H7B3I-DK", - "mcu_name": "STM32H7B3LIH6Q", - "description": "STM32H7B3I-DK with STM32H7B3LIH6Q", - "path": "repos/micropython/ports/stm32/boards/STM32H7B3I_DK/mpconfigboard.h" + "description": "TinyS3 with ESP32-S3-FN8", + "port": "esp32", + "board": "UM_TINYS3", + "board_name": "TinyS3", + "mcu_name": "ESP32-S3-FN8", + "path": "ports/esp32/boards/UM_TINYS3/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32L476DISC", - "board_name": "L476-DISCO", - "mcu_name": "STM32L476", - "description": "L476-DISCO with STM32L476", - "path": "repos/micropython/ports/stm32/boards/STM32L476DISC/mpconfigboard.h" + "description": "TinyWATCH S3 with ESP32-S3-PICO-1-N8R2", + "port": "esp32", + "board": "UM_TINYWATCHS3", + "board_name": "TinyWATCH S3", + "mcu_name": "ESP32-S3-PICO-1-N8R2", + "path": "ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.h", + "version": "v1.22.0" }, { - "port": "stm32", - "board": "STM32L496GDISC", - "board_name": "L496G-DISCO", - "mcu_name": "STM32L496", - "description": "L496G-DISCO with STM32L496", - "path": "repos/micropython/ports/stm32/boards/STM32L496GDISC/mpconfigboard.h" + "description": "Trinket M0 with SAMD21E18A", + "port": "samd", + "board": "ADAFRUIT_TRINKET_M0", + "board_name": "Trinket M0", + "mcu_name": "SAMD21E18A", + "path": "ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "USBDongle-WB55 with STM32WB55CGU6", "port": "stm32", "board": "USBDONGLE_WB55", "board_name": "USBDongle-WB55", "mcu_name": "STM32WB55CGU6", - "description": "USBDongle-WB55 with STM32WB55CGU6", - "path": "repos/micropython/ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h" + "path": "ports/stm32/boards/USBDONGLE_WB55/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "VCC-GND STM32F407VE with STM32F407VE", "port": "stm32", "board": "VCC_GND_F407VE", "board_name": "VCC-GND STM32F407VE", "mcu_name": "STM32F407VE", - "description": "VCC-GND STM32F407VE with STM32F407VE", - "path": "repos/micropython/ports/stm32/boards/VCC_GND_F407VE/mpconfigboard.h" + "path": "ports/stm32/boards/VCC_GND_F407VE/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "VCC-GND STM32F407ZG with STM32F407ZG", "port": "stm32", "board": "VCC_GND_F407ZG", "board_name": "VCC-GND STM32F407ZG", "mcu_name": "STM32F407ZG", - "description": "VCC-GND STM32F407ZG with STM32F407ZG", - "path": "repos/micropython/ports/stm32/boards/VCC_GND_F407ZG/mpconfigboard.h" + "path": "ports/stm32/boards/VCC_GND_F407ZG/mpconfigboard.h", + "version": "v1.22.0" }, { + "description": "VCC-GND STM32H743VI with STM32H743VI", "port": "stm32", "board": "VCC_GND_H743VI", "board_name": "VCC-GND STM32H743VI", "mcu_name": "STM32H743VI", - "description": "VCC-GND STM32H743VI with STM32H743VI", - "path": "repos/micropython/ports/stm32/boards/VCC_GND_H743VI/mpconfigboard.h" + "path": "ports/stm32/boards/VCC_GND_H743VI/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "VK-RA6M5 with RA6M5", + "port": "renesas-ra", + "board": "VK_RA6M5", + "board_name": "VK-RA6M5", + "mcu_name": "RA6M5", + "path": "ports/renesas-ra/boards/VK_RA6M5/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "W5100S-EVB-Pico", + "port": "rp2", + "board": "W5100S_EVB_PICO", + "board_name": "W5100S-EVB-Pico", + "mcu_name": "-", + "path": "ports/rp2/boards/W5100S_EVB_PICO/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "W5500-EVB-Pico", + "port": "rp2", + "board": "W5500_EVB_PICO", + "board_name": "W5500-EVB-Pico", + "mcu_name": "-", + "path": "ports/rp2/boards/W5500_EVB_PICO/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "WeAct Studio RP2040", + "port": "rp2", + "board": "WEACTSTUDIO", + "board_name": "WeAct Studio RP2040", + "mcu_name": "-", + "path": "ports/rp2/boards/WEACTSTUDIO/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "Wio Terminal D51R with SAMD51P19A", + "port": "samd", + "board": "SEEED_WIO_TERMINAL", + "board_name": "Wio Terminal D51R", + "mcu_name": "SAMD51P19A", + "path": "ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "WiPy with CC3200", + "port": "cc3200", + "board": "WIPY", + "board_name": "WiPy", + "mcu_name": "CC3200", + "path": "ports/cc3200/boards/WIPY/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "WT51822-S4AT with NRF51822", + "port": "nrf", + "board": "WT51822_S4AT", + "board_name": "WT51822-S4AT", + "mcu_name": "NRF51822", + "path": "ports/nrf/boards/WT51822_S4AT/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "XENON with NRF52840", + "port": "nrf", + "board": "PARTICLE_XENON", + "board_name": "XENON", + "mcu_name": "NRF52840", + "path": "ports/nrf/boards/PARTICLE_XENON/mpconfigboard.h", + "version": "v1.22.0" + }, + { + "description": "XIAO nRF52840 Sense with NRF52840", + "port": "nrf", + "board": "SEEED_XIAO_NRF52", + "board_name": "XIAO nRF52840 Sense", + "mcu_name": "NRF52840", + "path": "ports/nrf/boards/SEEED_XIAO_NRF52/mpconfigboard.h", + "version": "v1.22.0" } ] \ No newline at end of file diff --git a/src/stubber/freeze/freeze_manifest_2.py b/src/stubber/freeze/freeze_manifest_2.py index 125f3f5b5..157cdab14 100644 --- a/src/stubber/freeze/freeze_manifest_2.py +++ b/src/stubber/freeze/freeze_manifest_2.py @@ -57,7 +57,8 @@ def freeze_one_manifest_2(manifest: Path, frozen_stub_path: Path, mpy_path: Path # so we need to get the port and board from the path log.info(f"input_manifest: {manifest}") port, board = get_portboard(manifest) - log.info(f"port-board: '{port}-{board}'") + + log.info("port-board: {}".format((port + "-" +board).rstrip("-"))) path_vars = make_path_vars(port=port, board=board, mpy_path=mpy_path, mpy_lib_path=mpy_lib_path) upy_manifest = ManifestFile(MODE_FREEZE, path_vars) diff --git a/src/stubber/freeze/get_frozen.py b/src/stubber/freeze/get_frozen.py index 3120aa89c..bf43c1333 100644 --- a/src/stubber/freeze/get_frozen.py +++ b/src/stubber/freeze/get_frozen.py @@ -24,14 +24,15 @@ from loguru import logger as log from packaging.version import Version + +import stubber.basicgit as git from stubber import utils +from stubber.codemod.add_comment import AddComment from stubber.freeze.freeze_folder import freeze_folders # Micropython < v1.12 from stubber.freeze.freeze_manifest_2 import freeze_one_manifest_2 from stubber.utils.config import CONFIG -from stubber.codemod.add_comment import AddComment - +from stubber.utils.versions import SET_PREVIEW, V_PREVIEW -# globals FAMILY = "micropython" @@ -54,13 +55,18 @@ def add_comment_to_path(path: Path, comment: str) -> None: Add a comment to the top of each file in the path using a codemod """ - #TODO: #305 add comment line to each file with the micropython version it was generated from + # TODO: #305 add comment line to each file with the micropython version it was generated from # frozen_stub_path # python -m libcst.tool codemod --include-stubs --no-format add_comment.AddComment .\repos\micropython-stubs\stubs\micropython-v1_19_1-frozen\ --comment "# Micropython 1.19.1 frozen stubs" pass -def freeze_any(stub_folder: Path, version: str, mpy_path: Optional[Path] = None, mpy_lib_path: Optional[Path] = None) -> int: +def freeze_any( + stub_folder: Optional[Path] = None, + version: str = V_PREVIEW, + mpy_path: Optional[Path] = None, + mpy_lib_path: Optional[Path] = None, +) -> Path: """ Get and parse the to-be-frozen .py modules for micropython to extract the static type information - requires that the MicroPython and Micropython-lib repos are checked out and available on a local path @@ -73,24 +79,24 @@ def freeze_any(stub_folder: Path, version: str, mpy_path: Optional[Path] = None, current_dir = os.getcwd() mpy_path = Path(mpy_path).absolute() if mpy_path else CONFIG.mpy_path.absolute() mpy_lib_path = Path(mpy_lib_path).absolute() if mpy_lib_path else CONFIG.mpy_path.absolute() - if not stub_folder: - frozen_stub_path = Path("{}/{}_{}_frozen".format(CONFIG.stub_path, FAMILY, utils.clean_version(version, flat=True))).absolute() - else: - frozen_stub_path: Path = Path(stub_folder).absolute() # if old version of micropython, use the old freeze method - if version not in ["master", "latest"] and Version(version) <= Version("1.11"): + if version not in SET_PREVIEW and Version(version) <= Version("1.11"): + frozen_stub_path = get_fsp(version, stub_folder) log.debug("MicroPython v1.11, older or other") # others modules = freeze_folders(frozen_stub_path.as_posix(), mpy_path.as_posix(), mpy_lib_path.as_posix(), version) count = len(modules) else: + # get the current checked out version + version = utils.checkedout_version(CONFIG.mpy_path) + + frozen_stub_path = get_fsp(version, stub_folder) # get the manifests of the different ports and boards all_manifests = get_manifests(mpy_path) # process all_manifests under the ports folder and update the frozen files in the stubs folder - # we are goning to jump around, avoid relative paths - frozen_stub_path = frozen_stub_path.absolute() + # we are going to jump around, avoid relative paths mpy_path = mpy_path.absolute() mpy_lib_path = mpy_lib_path.absolute() @@ -111,4 +117,13 @@ def freeze_any(stub_folder: Path, version: str, mpy_path: Optional[Path] = None, # restore cwd os.chdir(current_dir) - return count + return frozen_stub_path + + +def get_fsp(version: str, stub_folder: Optional[Path] = None) -> Path: + if not stub_folder: + frozen_stub_path = CONFIG.stub_path / f"{FAMILY}-{utils.clean_version(version, flat=True)}-frozen" + frozen_stub_path = frozen_stub_path.absolute() + else: + frozen_stub_path: Path = Path(stub_folder).absolute() + return frozen_stub_path diff --git a/src/stubber/minify.py b/src/stubber/minify.py index 98922a939..c3f77c3c2 100644 --- a/src/stubber/minify.py +++ b/src/stubber/minify.py @@ -13,6 +13,8 @@ import python_minifier from loguru import logger as log +from stubber.utils.versions import SET_PREVIEW, V_PREVIEW + # Type Aliases for minify StubSource = Union[Path, str, StringIO, TextIOWrapper] XCompileDest = Union[Path, BytesIO] @@ -192,19 +194,62 @@ def minify_script(source_script: StubSource, keep_report: bool = True, diff: boo source_content = "" if isinstance(source_script, Path): - source_content = source_script.read_text() + source_content = source_script.read_text(encoding="utf-8") elif isinstance(source_script, (StringIO, TextIOWrapper)): source_content = "".join(source_script.readlines()) elif isinstance(source_script, str): # type: ignore source_content = source_script else: - raise TypeError( - f"source_script must be str, Path, or file-like object, not {type(source_script)}" - ) + raise TypeError(f"source_script must be str, Path, or file-like object, not {type(source_script)}") if not source_content: raise ValueError("No source content") + len_1 = len(source_content) + + if 0: + min_source = reduce_log_print(keep_report, diff, source_content) + else: + min_source = source_content + len_2 = len(min_source) + + min_source = python_minifier.minify( + min_source, + filename=getattr(source_script, "name", None), + combine_imports=True, + remove_literal_statements=True, # no Docstrings + remove_annotations=True, # not used runtime anyways + hoist_literals=True, # remove redundant strings + rename_locals=True, # short names save memory + preserve_locals=["stubber", "path"], # names to keep + rename_globals=True, # short names save memory + # keep these globals to allow testing/mocking to work against the minified not compiled version + preserve_globals=[ + "main", + "Stubber", + "read_path", + "get_root", + "_info", + "os", + "sys", + "__version__", + ], + # remove_pass=True, # no dead code + # convert_posargs_to_args=True, # Does not save any space + ) + len_3 = len(min_source) + if 1: + # write to temp file for debugging + with open("tmp_minified.py", "w+") as f: + f.write(min_source) + + log.info(f"Original length : {len_1}") + log.info(f"Reduced length : {len_2}") + log.info(f"Minified length : {len_3}") + log.info(f"Reduced by : {len_1-len_3} ") + return min_source + +def reduce_log_print(keep_report, diff, source_content): edits: LineEdits = [ ("keepprint", "print('Debug: "), ("keepprint", "print('DEBUG: "), @@ -250,40 +295,7 @@ def minify_script(source_script: StubSource, keep_report: bool = True, diff: boo ] + edits content = edit_lines(source_content, edits, diff=diff) - - if 1: - # write to temp file for debugging - with open("tmp_minified.py", "w+") as f: - f.write(content) - - source = python_minifier.minify( - content, - filename=getattr(source_script, "name", None), - combine_imports=True, - remove_literal_statements=True, # no Docstrings - remove_annotations=True, # not used runtime anyways - hoist_literals=True, # remove redundant strings - rename_locals=True, # short names save memory - preserve_locals=["stubber", "path"], # names to keep - rename_globals=True, # short names save memory - # keep these globals to allow testing/mocking to work against the minified not compiled version - preserve_globals=[ - "main", - "Stubber", - "read_path", - "get_root", - "_info", - "os", - "sys", - "__version__", - ], - # remove_pass=True, # no dead code - # convert_posargs_to_args=True, # Does not save any space - ) - log.debug(f"Original length : {len(content)}") - log.info(f"Minified length : {len(source)}") - log.info(f"Reduced by : {len(content)-len(source)} ") - return source + return content def minify( @@ -347,11 +359,10 @@ def cross_compile( else: # target must be a Path object _target = get_temp_file(suffix=".mpy") - result = pipx_mpy_cross(version, source_file, _target) if result.stderr and "No matching distribution found for mpy-cross==" in result.stderr: log.warning(f"mpy-cross=={version} not found, using latest") - result = pipx_mpy_cross("latest", source_file, _target) + result = pipx_mpy_cross(V_PREVIEW, source_file, _target) if result.returncode == 0: log.debug(f"mpy-cross compiled to : {_target.name}") @@ -367,9 +378,11 @@ def cross_compile( return result.returncode -def pipx_mpy_cross(version, source_file, _target): +def pipx_mpy_cross(version: str, source_file, _target): """Run mpy-cross using pipx""" - if version == "latest": + + log.info(f"Compiling with mpy-cross version: {version}") + if version in SET_PREVIEW: version = "" if version: version = "==" + version @@ -378,7 +391,7 @@ def pipx_mpy_cross(version, source_file, _target): # Add params cmd += ["-O2", str(source_file), "-o", str(_target), "-s", "createstubs.py"] log.trace(" ".join(cmd)) - result = subprocess.run(cmd, capture_output=True, text=True) + result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8") # Specify the encoding return result diff --git a/src/stubber/publish/candidates.py b/src/stubber/publish/candidates.py index 1baf5b49b..75274277d 100644 --- a/src/stubber/publish/candidates.py +++ b/src/stubber/publish/candidates.py @@ -10,7 +10,6 @@ - get a list of the firmware/board stubs (firmware candidates) """ - import re from pathlib import Path from typing import Any, Dict, Generator, List, Optional, Union @@ -18,15 +17,11 @@ from packaging.version import parse import stubber.basicgit as git -from stubber.publish.enums import COMBO_STUBS, DOC_STUBS, FIRMWARE_STUBS +from stubber import utils from stubber.publish.defaults import GENERIC, GENERIC_L, GENERIC_U +from stubber.publish.enums import COMBO_STUBS, DOC_STUBS, FIRMWARE_STUBS from stubber.utils.config import CONFIG -from stubber.utils.versions import clean_version, micropython_versions - -OLDEST_VERSION = "1.16" -"This is the oldest MicroPython version to build the stubs on" - -V_LATEST = "latest" +from stubber.utils.versions import OLDEST_VERSION, SET_PREVIEW, V_PREVIEW, clean_version, micropython_versions def subfolder_names(path: Path): @@ -50,13 +45,13 @@ def version_candidates( for name in subfolder_names(path): if match := re.match(folder_re, name): folder_ver = clean_version(match[1]) - if folder_ver == V_LATEST or parse(folder_ver) >= parse(oldest): + if folder_ver == V_PREVIEW or parse(folder_ver) >= parse(oldest): yield folder_ver def list_frozen_ports( family: str = "micropython", - version: str = V_LATEST, + version: str = V_PREVIEW, path: Path = CONFIG.stub_path, ): "get list of ports with frozen stubs for a given family and version" @@ -94,7 +89,7 @@ def list_micropython_port_boards( def frozen_candidates( family: str = "micropython", - versions: Union[str, List[str]] = V_LATEST, + versions: Union[str, List[str]] = V_PREVIEW, ports: Union[str, List[str]] = "all", boards: Union[str, List[str]] = "all", *, @@ -111,7 +106,7 @@ def frozen_candidates( auto_port = is_auto(ports) auto_board = is_auto(boards) if is_auto(versions): - versions = list(version_candidates(suffix="frozen", prefix=family, path=path)) + [V_LATEST] + versions = list(version_candidates(suffix="frozen", prefix=family, path=path)) + [V_PREVIEW] else: versions = [versions] if isinstance(versions, str) else versions @@ -129,9 +124,7 @@ def frozen_candidates( # lookup the (frozen) micropython ports ports = list_frozen_ports(family, version, path=path) else: - raise NotImplementedError( - f"auto ports not implemented for family {family}" - ) # pragma: no cover + raise NotImplementedError(f"auto ports not implemented for family {family}") # pragma: no cover # elif family == "pycom": # ports = ["esp32"] # elif family == "lobo": @@ -164,9 +157,7 @@ def frozen_candidates( else: # raise NotImplementedError(f"auto boards not implemented for family {family}") # pragma: no cover - raise NotImplementedError( - f"auto boards not implemented for family {family}" - ) # pragma: no cover + raise NotImplementedError(f"auto boards not implemented for family {family}") # pragma: no cover # elif family == "pycom": # boards = ["wipy", "lopy", "gpy", "fipy"] # --------------------------------------------------------------------------- @@ -199,7 +190,7 @@ def is_auto(thing: Union[None, str, List[str]]): def docstub_candidates( family: str = "micropython", - versions: Union[str, List[str]] = V_LATEST, + versions: Union[str, List[str]] = V_PREVIEW, path: Path = CONFIG.stub_path, ): """ @@ -220,7 +211,7 @@ def docstub_candidates( def board_candidates( family: str = "micropython", - versions: Union[str, List[str]] = V_LATEST, + versions: Union[str, List[str]] = V_PREVIEW, *, mpy_path: Path = CONFIG.mpy_path, pt: str = FIRMWARE_STUBS, @@ -237,8 +228,10 @@ def board_candidates( for version in versions: # check out the micropython repo for this version - if version in ["latest", "master"]: + if version in SET_PREVIEW: r = git.switch_branch(repo=mpy_path, branch="master") + # get the current checked out version + version = utils.checkedout_version(mpy_path) else: r = git.checkout_tag(repo=mpy_path, tag=version) if not r: @@ -283,7 +276,6 @@ def filter_list( worklist = [ i for i in worklist - if i["board"].lower() in boards_ - or i["board"].lower().replace("generic_", "") in boards_ + if i["board"].lower() in boards_ or i["board"].lower().replace("generic_", "") in boards_ ] return worklist diff --git a/src/stubber/publish/defaults.py b/src/stubber/publish/defaults.py index d507e7dd4..b74a31042 100644 --- a/src/stubber/publish/defaults.py +++ b/src/stubber/publish/defaults.py @@ -2,7 +2,7 @@ from typing import Dict, List from stubber.utils.config import CONFIG -from stubber.utils.versions import clean_version +from stubber.utils.versions import V_PREVIEW, clean_version # The default board for the ports modules documented with base name only # as the MicroPython BOARD naming convention has changed over time there are different options to try @@ -24,7 +24,7 @@ "GENERIC eithercase" -def default_board(port: str, version="latest") -> str: # sourcery skip: assign-if-exp +def default_board(port: str, version=V_PREVIEW) -> str: # sourcery skip: assign-if-exp """Return the default board for the given version and port""" ver_flat = clean_version(version, flat=True) if port in DEFAULT_BOARDS: diff --git a/src/stubber/publish/merge_docstubs.py b/src/stubber/publish/merge_docstubs.py index d5a3c9012..f1934a7d6 100644 --- a/src/stubber/publish/merge_docstubs.py +++ b/src/stubber/publish/merge_docstubs.py @@ -49,9 +49,7 @@ def merge_all_docstubs( for candidate in candidates: # use the default board for the port if candidate["board"] in GENERIC: - candidate["board"] = default_board( - port=candidate["port"], version=candidate["version"] - ) + candidate["board"] = default_board(port=candidate["port"], version=candidate["version"]) # check if we have board stubs of this version and port doc_path = CONFIG.stub_path / f"{get_base(candidate)}-docstubs" # src and dest paths @@ -120,13 +118,13 @@ def copy_and_merge_docstubs(fw_path: Path, dest_path: Path, docstub_path: Path): "pycopy_imphook", # is not intended to be used directly, and has an unresolved subclass ]: for suffix in [".py", ".pyi"]: - if (dest_path / name).with_suffix(suffix).exists(): - (dest_path / name).with_suffix(suffix).unlink() + if (dest_path / name).with_suffix(suffix).exists(): # type: ignore + (dest_path / name).with_suffix(suffix).unlink() # type: ignore # 2 - Enrich the firmware stubs with the document stubs result = enrich_folder(dest_path, docstub_path=docstub_path, write_back=True) # copy the docstubs manifest.json file to the package folder - # if (docstub_path / "modules.json").exists(): - shutil.copy(docstub_path / "modules.json", dest_path / "doc_stubs.json") + if (docstub_path / "modules.json").exists(): + shutil.copy(docstub_path / "modules.json", dest_path / "doc_stubs.json") return result diff --git a/src/stubber/publish/missing_class_methods.py b/src/stubber/publish/missing_class_methods.py index fdb765ac5..4d35a8033 100644 --- a/src/stubber/publish/missing_class_methods.py +++ b/src/stubber/publish/missing_class_methods.py @@ -27,7 +27,7 @@ def add_machine_pin_call(merged_path: Path, version: str): log.error(f"no docstubs found for {version}") return False log.trace(f"Parsing {mod_path} for __call__ method") - source = mod_path.read_text() + source = mod_path.read_text(encoding="utf-8") module = cst.parse_module(source) call_finder = CallFinder() @@ -40,7 +40,7 @@ def add_machine_pin_call(merged_path: Path, version: str): # then use the CallAdder to add the __call__ method to all machine and pyb stubs mod_paths = [f for f in merged_path.rglob("*.*") if f.stem in {"machine", "umachine", "pyb"}] for mod_path in mod_paths: - source = mod_path.read_text() + source = mod_path.read_text(encoding="utf-8") machine_module = cst.parse_module(source) new_module = machine_module.visit(CallAdder(call_finder.call_meth)) mod_path.write_text(new_module.code) diff --git a/src/stubber/publish/pathnames.py b/src/stubber/publish/pathnames.py index d3960976f..f0061e4bc 100644 --- a/src/stubber/publish/pathnames.py +++ b/src/stubber/publish/pathnames.py @@ -11,7 +11,7 @@ from stubber.publish.defaults import default_board from stubber.publish.package import GENERIC from stubber.utils.config import CONFIG -from stubber.utils.versions import clean_version +from stubber.utils.versions import V_PREVIEW, clean_version ## Helper functions @@ -40,7 +40,7 @@ def board_folder_name(fw: Dict, *, version: Optional[str] = None) -> str: def get_board_path(candidate: Dict) -> Path: board_path = CONFIG.stub_path / board_folder_name(candidate) - if candidate["version"] == "latest" and not board_path.exists(): + if V_PREVIEW in candidate["version"] and not board_path.exists(): log.debug(f"no board stubs found for {candidate['version']}, trying stable") board_path = CONFIG.stub_path / board_folder_name(candidate, version=CONFIG.stable_version) diff --git a/src/stubber/publish/publish.py b/src/stubber/publish/publish.py index 66749a5a3..0529d9ab4 100644 --- a/src/stubber/publish/publish.py +++ b/src/stubber/publish/publish.py @@ -13,11 +13,12 @@ from stubber.publish.enums import COMBO_STUBS from stubber.publish.package import get_package from stubber.utils.config import CONFIG +from stubber.utils.versions import V_PREVIEW def build_multiple( family: str = "micropython", - versions: List[str] = ["v1.19.1"], + versions: List[str] = [V_PREVIEW], ports: List[str] = ["all"], boards: List[str] = [GENERIC_U], production: bool = False, diff --git a/src/stubber/publish/stubpackage.py b/src/stubber/publish/stubpackage.py index 4b6d47005..ceabdfd7c 100644 --- a/src/stubber/publish/stubpackage.py +++ b/src/stubber/publish/stubpackage.py @@ -29,7 +29,7 @@ from stubber.publish.enums import StubSource from stubber.publish.pypi import Version, get_pypi_versions from stubber.utils.config import CONFIG -from stubber.utils.versions import clean_version +from stubber.utils.versions import SET_PREVIEW, V_PREVIEW, clean_version Status = NewType("Status", Dict[str, Union[str, None]]) StubSources = List[Tuple[StubSource, Path]] @@ -108,7 +108,7 @@ def next_package_version(self, production: bool) -> str: return self._get_next_package_version(production) def is_preview(self): - return self.mpy_version == "latest" or "preview" in self.mpy_version + return self.mpy_version in SET_PREVIEW or V_PREVIEW in self.mpy_version def _get_next_preview_package_version(self, production: bool = False) -> str: """ @@ -125,17 +125,9 @@ def _get_next_preview_package_version(self, production: bool = False) -> str: parts = describe.split("-", 3) ver = parts[0] if len(parts) > 1: - rc = ( - parts[1] - if parts[1].isdigit() - else parts[2] - if len(parts) > 2 and parts[2].isdigit() - else 1 - ) + rc = parts[1] if parts[1].isdigit() else parts[2] if len(parts) > 2 and parts[2].isdigit() else 1 rc = int(rc) - base = ( - bump_version(Version(ver), minor_bump=True) if parts[1] != "preview" else Version(ver) - ) + base = bump_version(Version(ver), minor_bump=True) if parts[1] != V_PREVIEW else Version(ver) return str(bump_version(base, rc=rc)) # raise ValueError("cannot determine next version number micropython") @@ -312,9 +304,7 @@ def copy_stubs(self) -> None: # Check if all stub source folders exist for stub_type, src_path in self.stub_sources: if not (CONFIG.stub_path / src_path).exists(): - raise FileNotFoundError( - f"Could not find stub source folder {CONFIG.stub_path / src_path}" - ) + raise FileNotFoundError(f"Could not find stub source folder {CONFIG.stub_path / src_path}") # 1 - Copy the stubs to the package, directly in the package folder (no folders) # for stub_type, fw_path in [s for s in self.stub_sources]: @@ -325,9 +315,7 @@ def copy_stubs(self) -> None: self.copy_folder(stub_type, src_path) except OSError as e: if stub_type != StubSource.FROZEN: - raise FileNotFoundError( - f"Could not find stub source folder {src_path}" - ) from e + raise FileNotFoundError(f"Could not find stub source folder {src_path}") from e else: log.debug(f"Error copying stubs from : {CONFIG.stub_path / src_path}, {e}") finally: @@ -594,7 +582,7 @@ def pkg_version(self) -> str: with open(_toml, "rb") as f: pyproject = tomllib.load(f) ver = pyproject["tool"]["poetry"]["version"] - return str(parse(ver)) if ver != "latest" else ver + return str(parse(ver)) if ver not in SET_PREVIEW else ver @pkg_version.setter def pkg_version(self, version: str) -> None: @@ -654,6 +642,7 @@ def run_poetry(self, parameters: List[str]) -> bool: # stdout=subprocess.PIPE, stdout=subprocess.PIPE, # interestingly: errors on stdout , output on stderr ..... universal_newlines=True, + encoding="utf-8", ) log.trace(f"poetry {parameters} completed") except (NotADirectoryError, FileNotFoundError) as e: # pragma: no cover # InvalidVersion @@ -718,8 +707,7 @@ def update_pyproject_stubs(self) -> int: _pyproject = self.pyproject assert _pyproject is not None, "No pyproject.toml file found" _pyproject["tool"]["poetry"]["packages"] = [ - {"include": p.relative_to(self.package_path).as_posix()} - for p in sorted((self.package_path).rglob("*.pyi")) + {"include": p.relative_to(self.package_path).as_posix()} for p in sorted((self.package_path).rglob("*.pyi")) ] # write out the pyproject.toml file self.pyproject = _pyproject @@ -862,9 +850,7 @@ def update_distribution(self, production: bool) -> bool: # check if the sources exist ok = self.are_package_sources_available() if not ok: - log.debug( - f"{self.package_name}: skipping as one or more source stub folders are missing" - ) + log.debug(f"{self.package_name}: skipping as one or more source stub folders are missing") self.status["error"] = "Skipped, stub folder(s) missing" shutil.rmtree(self.package_path.as_posix()) self._publish = False # type: ignore @@ -886,9 +872,7 @@ def build_distribution( self, production: bool, # PyPI or Test-PyPi - USED TO FIND THE NEXT VERSION NUMBER force=False, # BUILD even if no changes - ) -> ( - bool - ): # sourcery skip: default-mutable-arg, extract-duplicate-method, require-parameter-annotation + ) -> bool: # sourcery skip: default-mutable-arg, extract-duplicate-method, require-parameter-annotation """ Build a package look up the previous package version in the dabase @@ -908,14 +892,14 @@ def build_distribution( if not self.status["error"]: self.status["error"] = "Could not build/update package" return False - # If there are changes to the package, then publish it - if self.is_changed(): - log.info(f"Found changes to package sources: {self.package_name} {self.pkg_version} ") - log.trace(f"Old hash {self.hash} != New hash {self.calculate_hash()}") - elif force: - log.info(f"Force build: {self.package_name} {self.pkg_version} ") + # If there are changes to the package, then publish it if self.is_changed() or force: + if force: + log.info(f"Force build: {self.package_name} {self.pkg_version} ") + else: + log.info(f"Found changes to package sources: {self.package_name} {self.pkg_version} ") + log.trace(f"Old hash {self.hash} != New hash {self.calculate_hash()}") # Build the distribution files old_ver = self.pkg_version self.pkg_version = self.next_package_version(production) @@ -975,10 +959,8 @@ def publish_distribution_ifchanged( self.next_package_version(production=production) # Publish the package to PyPi, Test-PyPi or Github if self.is_changed(): - if self.mpy_version == "latest" and production and not force: - log.warning( - "version: `latest` package will only be available on Github, and not published to PyPi." - ) + if self.mpy_version in SET_PREVIEW and production and not force: + log.warning("version: `latest` package will only be available on Github, and not published to PyPi.") self.status["result"] = "Published to GitHub" else: return self.publish_distribution(dry_run, production, db) @@ -1007,9 +989,7 @@ def publish_distribution(self, dry_run, production, db): if not dry_run: pub_ok = self.poetry_publish(production=production) else: - log.warning( - f"{self.package_name}: Dry run, not publishing to {'' if production else 'Test-'}PyPi" - ) + log.warning(f"{self.package_name}: Dry run, not publishing to {'' if production else 'Test-'}PyPi") pub_ok = True if not pub_ok: log.warning(f"{self.package_name}: Publish failed for {self.pkg_version}") diff --git a/src/stubber/rst/lookup.py b/src/stubber/rst/lookup.py index f090b5418..6829c56f5 100644 --- a/src/stubber/rst/lookup.py +++ b/src/stubber/rst/lookup.py @@ -20,6 +20,7 @@ # all possible Types needed for the stubs - exxess types should be removed later , and otherwise won't do much harm TYPING_IMPORT: List[str] = [ + "from __future__ import annotations", "from typing import IO, Any, Callable, Coroutine, Dict, Generator, Iterator, List, NoReturn, Optional, Tuple, Union, NamedTuple, TypeVar", "from _typeshed import Incomplete", ] @@ -483,17 +484,18 @@ class Fix: # List of classes and their parent classes that should be added to the class definition CHILD_PARENT_CLASS = { # machine - # "SoftSPI": "SPI", # BUG: SoftSPI is defined before SPI, so baseclass is not yet available + # SoftSPI is defined before SPI, so baseclass is not yet available - but in a .pyi that is OK + "SoftSPI": "SPI", "SoftI2C": "I2C", "Switch": "Pin", "Signal": "Pin", # uio # unclear regarding deprecation in python 3.12 # "IOBase": "IO", # DOCME not in documentation - "TextIOWrapper": "IO", - "FileIO": "IO", - "StringIO": "IO", - "BytesIO": "IO", - "BufferedWriter": "IOBase", # DOCME: not in documentation + "TextIOWrapper": "IO", # "TextIOBase, TextIO", # based on Stdlib + "FileIO": "IO", # "RawIOBase, BinaryIO", # based on Stdlib + "StringIO": "IO", # "BufferedIOBase, BinaryIO", # based on Stdlib + "BytesIO": "IO", # "BufferedIOBase, BinaryIO", # based on Stdlib + "BufferedWriter": "IOBase", # DOCME: not in documentation # "BufferedWriter": "BufferedIOBase", # based on Stdlib # uzlib # "DecompIO": "IO", # https://docs.python.org/3/library/typing.html#other-concrete-types # ------------------------------------------------------------------------------------- @@ -512,7 +514,7 @@ class Fix: "namedtuple": "tuple", "deque": "stdlib_deque", # ESPNow - "ESPNow": "ESPNowBase,Iterator", # causes issue with mypy + "ESPNow": "ESPNowBase,Iterator", # causes issue with mypy "AIOESPNow": "ESPNow", # array "array": "List", diff --git a/src/stubber/rst/reader.py b/src/stubber/rst/reader.py index 2304e2662..d6876725f 100644 --- a/src/stubber/rst/reader.py +++ b/src/stubber/rst/reader.py @@ -83,6 +83,7 @@ ) from stubber.rst.lookup import Fix from stubber.utils.config import CONFIG +from stubber.utils.versions import V_PREVIEW SEPERATOR = "::" @@ -506,7 +507,7 @@ def parse_module(self): # Add link to online documentation # https://docs.micropython.org/en/v1.17/library/array.html if "nightly" in self.source_tag: - version = "latest" + version = V_PREVIEW else: version = self.source_tag.replace( "_", "." diff --git a/src/stubber/stubber.py b/src/stubber/stubber.py index 706706c97..db831b0db 100644 --- a/src/stubber/stubber.py +++ b/src/stubber/stubber.py @@ -4,8 +4,8 @@ """Create, Process, and Maintain stubs ✏️ for MicroPython""" -from stubber.commands.cli import stubber_cli from stubber.commands.build_cmd import cli_build +from stubber.commands.cli import stubber_cli from stubber.commands.clone_cmd import cli_clone from stubber.commands.config_cmd import cli_config from stubber.commands.enrich_folder_cmd import cli_enrich_folder @@ -22,7 +22,6 @@ from stubber.commands.upd_module_list_cmd import cli_update_module_list from stubber.commands.variants_cmd import cli_variants - ########################################################################################## if __name__ == "__main__": # add all commands to the CLI @@ -33,13 +32,13 @@ stubber_cli.add_command(cli_docstubs) stubber_cli.add_command(cli_get_core) stubber_cli.add_command(cli_get_frozen) - stubber_cli.add_command(cli_get_lobo) + # stubber_cli.add_command(cli_get_lobo) stubber_cli.add_command(cli_stub) stubber_cli.add_command(cli_enrich_folder) - stubber_cli.add_command(cli_minify) + # stubber_cli.add_command(cli_minify) stubber_cli.add_command(cli_publish) stubber_cli.add_command(cli_merge_docstubs) - stubber_cli.add_command(cli_update_module_list) - stubber_cli.add_command(cli_update_fallback) + # stubber_cli.add_command(cli_update_module_list) + # stubber_cli.add_command(cli_update_fallback) stubber_cli.add_command(cli_variants) stubber_cli() diff --git a/src/stubber/update_fallback.py b/src/stubber/update_fallback.py index f61302b6f..d55768101 100644 --- a/src/stubber/update_fallback.py +++ b/src/stubber/update_fallback.py @@ -9,6 +9,8 @@ from loguru import logger as log +from stubber.utils.versions import V_PREVIEW + # log = logging.getLogger() RELEASED = "v1_18" @@ -24,7 +26,7 @@ def fallback_sources(version: str, fw_version: Optional[str] = None) -> List[Tup """ if not fw_version: fw_version = version - if fw_version == "latest": + if fw_version == V_PREVIEW: fw_version = RELEASED SOURCES = [ ("uasyncio", f"micropython-{fw_version}-esp32"), diff --git a/src/stubber/update_module_list.py b/src/stubber/update_module_list.py index c63b5c149..214ab14f4 100644 --- a/src/stubber/update_module_list.py +++ b/src/stubber/update_module_list.py @@ -100,7 +100,7 @@ def update_module_list(): "builtins", "re", } - + log.info("Update the module list in createstubs.py") all_modules = read_modules() modules_to_stub = sorted(all_modules - set(mods_excluded | mods_problematic)) diff --git a/src/stubber/utils/__init__.py b/src/stubber/utils/__init__.py index a1af5e04c..7188d95d7 100644 --- a/src/stubber/utils/__init__.py +++ b/src/stubber/utils/__init__.py @@ -2,4 +2,4 @@ from .manifest import make_manifest, manifest from .post import do_post_processing from .stubmaker import generate_pyi_files, generate_pyi_from_file -from .versions import clean_version +from .versions import checkedout_version, clean_version diff --git a/src/stubber/utils/config.py b/src/stubber/utils/config.py index d4b31f818..d5232a47b 100644 --- a/src/stubber/utils/config.py +++ b/src/stubber/utils/config.py @@ -2,12 +2,14 @@ from pathlib import Path +from loguru import logger as log from typedconfig.config import Config, key, section from typedconfig.source import EnvironmentConfigSource -from .typed_config_toml import TomlConfigSource -from loguru import logger as log import stubber.basicgit as git +from stubber.utils.versions import V_PREVIEW + +from .typed_config_toml import TomlConfigSource @section("micropython-stubber") @@ -16,9 +18,7 @@ class StubberConfig(Config): stub_path = key(key_name="stub-path", cast=Path, required=False, default=Path("./stubs")) "a Path to the stubs directory" # relative to stubs folder - fallback_path = key( - key_name="fallback-path", cast=Path, required=False, default=Path("typings/fallback") - ) + fallback_path = key(key_name="fallback-path", cast=Path, required=False, default=Path("typings/fallback")) "a Path to the fallback stubs directory" # ------------------------------------------------------------------------------------------ @@ -29,9 +29,7 @@ class StubberConfig(Config): mpy_path = key(key_name="mpy-path", cast=Path, required=False, default=Path("micropython")) "a Path to the micropython folder in the repos directory" - mpy_lib_path = key( - key_name="mpy-lib-path", cast=Path, required=False, default=Path("micropython-lib") - ) + mpy_lib_path = key(key_name="mpy-lib-path", cast=Path, required=False, default=Path("micropython-lib")) "a Path to the micropython-lib folder in the repos directory" # mpy_stubs_repo_path = key(key_name="mpy-stubs-repo-path", cast=Path, required=False, default=Path("./micropython-stubs")) @@ -96,7 +94,7 @@ def post_read_hook(self) -> dict: all_versions = ["1.19", "1.19.1", "1.20.0", "1.21.0"] config_updates.update(all_versions=all_versions) config_updates.update( - stable_version=[v for v in all_versions if not v.endswith("preview")][-1] + stable_version=[v for v in all_versions if not v.endswith(V_PREVIEW)][-1] ) # second last version - last version is the preview version return config_updates diff --git a/src/stubber/utils/post.py b/src/stubber/utils/post.py index a403e3dec..29e007996 100644 --- a/src/stubber/utils/post.py +++ b/src/stubber/utils/post.py @@ -35,7 +35,7 @@ def run_black(path: Path, capture_output: bool = False): "--line-length", "140", ] - result = subprocess.run(cmd, capture_output=True, text=True) + result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8") return result.returncode diff --git a/src/stubber/utils/repos.py b/src/stubber/utils/repos.py index 82497d4a2..2e39544af 100644 --- a/src/stubber/utils/repos.py +++ b/src/stubber/utils/repos.py @@ -13,6 +13,7 @@ import stubber.basicgit as git from stubber.utils.config import CONFIG +from stubber.utils.versions import SET_PREVIEW, V_PREVIEW # # log = logging.getLogger(__name__) @@ -34,8 +35,8 @@ def switch(tag: str, *, mpy_path: Path, mpy_lib_path: Path): git.fetch(mpy_lib_path) if not tag or tag in {"master", ""}: - tag = "latest" - if tag == "latest": + tag = V_PREVIEW + if tag in SET_PREVIEW: git.switch_branch(repo=mpy_path, branch="master") else: git.checkout_tag(repo=mpy_path, tag=tag) @@ -76,7 +77,7 @@ def match_lib_with_mpy(version_tag: str, mpy_path: Path, lib_path: Path) -> bool micropython_lib_commits = read_micropython_lib_commits() # Make sure that the correct micropython-lib release is checked out # check if micropython-lib has matching tags - if version_tag == "latest": + if version_tag in SET_PREVIEW: # micropython-lib is now a submodule result = git.checkout_commit("master", lib_path) if not result: @@ -88,8 +89,10 @@ def match_lib_with_mpy(version_tag: str, mpy_path: Path, lib_path: Path) -> bool # micropython-lib is now a submodule result = git.checkout_tag(version_tag, lib_path) if not result: - log.error("Could not checkout micropython-lib @master") - return False + log.warning(f"Could not checkout micropython-lib @{version_tag}") + if not git.checkout_tag("master", lib_path): + log.error("Could not checkout micropython-lib @master") + return False return git.sync_submodules(mpy_path) else: log.info( @@ -109,10 +112,10 @@ def fetch_repos(tag: str, mpy_path: Path, mpy_lib_path: Path): log.trace("no stubs repo found : {CONFIG.stub_path.parent}") if not tag: - tag = "latest" + tag = V_PREVIEW log.info(f"Switching to {tag}") - if tag == "latest": + if tag == V_PREVIEW: git.switch_branch(repo=mpy_path, branch="master") else: git.checkout_tag(repo=mpy_path, tag=tag) diff --git a/src/stubber/utils/versions.py b/src/stubber/utils/versions.py index d1494670c..37b5267f8 100644 --- a/src/stubber/utils/versions.py +++ b/src/stubber/utils/versions.py @@ -1,8 +1,21 @@ """Handle versions of micropython based on the git tags in the repo """ +from pathlib import Path + from github import Github from packaging.version import parse +import stubber.basicgit as git +import stubber.utils as utils + +OLDEST_VERSION = "1.16" +"This is the oldest MicroPython version to build the stubs on" + +V_PREVIEW = "preview" +"Latest preview version" + +SET_PREVIEW = {"preview", "latest", "master"} + def clean_version( version: str, @@ -15,9 +28,9 @@ def clean_version( ): "Clean up and transform the many flavours of versions" # 'v1.13.0-103-gb137d064e' --> 'v1.13-103' - if version in {"", "-"}: return version + is_preview = "-preview" in version nibbles = version.split("-") ver_ = nibbles[0].lower().lstrip("v") if not patch and ver_ >= "1.10.0" and ver_ < "1.20.0" and ver_.endswith(".0"): @@ -25,21 +38,35 @@ def clean_version( nibbles[0] = nibbles[0][:-2] if len(nibbles) == 1: version = nibbles[0] - elif build: + elif build and not is_preview: version = "-".join(nibbles) if commit else "-".join(nibbles[:-1]) else: # version = "-".join((nibbles[0], LATEST)) # HACK: this is not always right, but good enough most of the time - version = "latest" + if is_preview: + version = "-".join((nibbles[0], V_PREVIEW)) + else: + version = V_PREVIEW if flat: - version = version.strip().replace(".", "_") + version = version.strip().replace(".", "_").replace("-", "_") else: - version = version.strip().replace("_", ".") + version = version.strip().replace("_preview", "-preview").replace("_", ".") if drop_v: version = version.lstrip("v") - elif not version.startswith("v") and version.lower() != "latest": + elif not version.startswith("v") and version.lower() not in SET_PREVIEW: version = "v" + version + if version == "latest": + version = V_PREVIEW + return version + + +def checkedout_version(path: Path, flat: bool = False) -> str: + """Get the checked-out version of the repo""" + version = git.get_local_tag(path.as_posix()) + if not version: + raise ValueError("No valid Tag found") + version = utils.clean_version(version, flat=flat, drop_v=False) return version @@ -50,4 +77,18 @@ def micropython_versions(start: str = "v1.9.2"): repo = g.get_repo("micropython/micropython") return [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(start)] except Exception: - return ["v1.19.1", "v1.19", "v1.18", "v1.17", "v1.16", "v1.15", "v1.14", "v1.13", "v1.12", "v1.11", "v1.10", "v1.9.4", "v1.9.3"] + return [ + "v1.19.1", + "v1.19", + "v1.18", + "v1.17", + "v1.16", + "v1.15", + "v1.14", + "v1.13", + "v1.12", + "v1.11", + "v1.10", + "v1.9.4", + "v1.9.3", + ] diff --git a/src/stubber/variants.py b/src/stubber/variants.py index ac0e1640f..bb50cf8b0 100644 --- a/src/stubber/variants.py +++ b/src/stubber/variants.py @@ -26,7 +26,7 @@ def create_variants( *, target_path: Optional[Path] = None, version: str = "", - make_variants: List[CreateStubsVariant] = ALL_VARIANTS, + make_variants: List[CreateStubsVariant] = ALL_VARIANTS[:3], update_modules: bool = True, ): """ @@ -50,7 +50,7 @@ def create_variants( ctx = codemod.CodemodContext() base_file = base_path / "createstubs.py" log.info(f"Reading : {base_file}") - base_txt = (base_path / "createstubs.py").read_text() + base_txt = (base_path / "createstubs.py").read_text(encoding="utf-8") base_module = cst.parse_module(base_txt) for var in make_variants: @@ -89,7 +89,7 @@ def create_variants( # str -> path # read minified file - minified_txt = minified_path.read_text() + minified_txt = minified_path.read_text(encoding="utf-8") cross_compile(minified_txt, mpy_path, version=version) diff --git a/tests/commandline/stubber_cli_test.py b/tests/commandline/stubber_cli_test.py index bba9814ca..9fdecdbd3 100644 --- a/tests/commandline/stubber_cli_test.py +++ b/tests/commandline/stubber_cli_test.py @@ -42,12 +42,8 @@ def test_cmd_get_config(): def test_cmd_clone(mocker: MockerFixture, tmp_path: Path): runner = CliRunner() # from stubber.commands.clone import git - m_clone: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0 - ) - m_fetch: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0 - ) + m_clone = mocker.patch("stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0) + m_fetch = mocker.patch("stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0) result = runner.invoke(stubber.stubber_cli, ["clone"]) assert result.exit_code == 0 @@ -70,9 +66,7 @@ def test_cmd_clone(mocker: MockerFixture, tmp_path: Path): @pytest.mark.mocked def test_cmd_clone_path(mocker: MockerFixture, tmp_path: Path): runner = CliRunner() - m_clone: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0 - ) + m_clone = mocker.patch("stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0) m_tag = mocker.patch("stubber.commands.clone_cmd.git.get_local_tag", autospec=True) m_dir = mocker.patch("stubber.commands.clone_cmd.os.mkdir", autospec=True) # type: ignore @@ -101,6 +95,7 @@ def test_cmd_clone_path(mocker: MockerFixture, tmp_path: Path): @pytest.mark.parametrize( "params", [ + pytest.param(["switch", "preview"], id="preview"), pytest.param(["switch", "latest"], id="latest"), pytest.param(["switch", "v1.17"], id="v1.17"), pytest.param(["switch", "v1.9.4"], id="v1.9.4"), @@ -111,23 +106,13 @@ def test_cmd_switch(mocker: MockerFixture, params: List[str]): runner = CliRunner() # Mock Path.exists mocker.patch("stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0) - m_fetch: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0 - ) + m_fetch = mocker.patch("stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0) - m_switch: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0 - ) - m_checkout: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.checkout_tag", autospec=True, return_value=0 - ) - mocker.patch( - "stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42" - ) + m_switch = mocker.patch("stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0) + m_checkout = mocker.patch("stubber.commands.clone_cmd.git.checkout_tag", autospec=True, return_value=0) + mocker.patch("stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42") - m_match = mocker.patch( - "stubber.utils.repos.match_lib_with_mpy", autospec=True - ) # Moved to other module + m_match = mocker.patch("stubber.utils.repos.match_lib_with_mpy", autospec=True) # Moved to other module mocker.patch("stubber.commands.clone_cmd.Path.exists", return_value=True) result = runner.invoke(stubber.stubber_cli, params) @@ -142,7 +127,7 @@ def test_cmd_switch(mocker: MockerFixture, params: List[str]): # core m_match.assert_called_once() - if "latest" in params: + if "latest" in params or "preview" in params: m_switch.assert_called_once() m_checkout.assert_not_called() else: @@ -155,22 +140,12 @@ def test_cmd_switch(mocker: MockerFixture, params: List[str]): def test_cmd_switch_version(mocker: MockerFixture, version: str): runner = CliRunner() # Mock Path.exists - m_clone: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0 - ) - m_fetch: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0 - ) + m_clone = mocker.patch("stubber.commands.clone_cmd.git.clone", autospec=True, return_value=0) + m_fetch = mocker.patch("stubber.commands.clone_cmd.git.fetch", autospec=True, return_value=0) - m_switch: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0 - ) - m_checkout: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.checkout_tag", autospec=True, return_value=0 - ) - m_get_l_tag: MagicMock = mocker.patch( - "stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42" - ) + m_switch = mocker.patch("stubber.commands.clone_cmd.git.switch_branch", autospec=True, return_value=0) + m_checkout = mocker.patch("stubber.commands.clone_cmd.git.checkout_tag", autospec=True, return_value=0) + m_get_l_tag = mocker.patch("stubber.commands.clone_cmd.git.get_local_tag", autospec=True, return_value="v1.42") m_match = mocker.patch("stubber.utils.repos.match_lib_with_mpy", autospec=True) @@ -197,28 +172,28 @@ def test_cmd_switch_version(mocker: MockerFixture, version: str): ########################################################################################## # minify ########################################################################################## -@pytest.mark.mocked -def test_cmd_minify(mocker: MockerFixture): - # check basic command line sanity check - runner = CliRunner() - mock_minify: MagicMock = mocker.MagicMock(return_value=0) - mocker.patch("stubber.commands.minify_cmd.minify", mock_minify) +# @pytest.mark.mocked +# def test_cmd_minify(mocker: MockerFixture): +# # check basic command line sanity check +# runner = CliRunner() +# mock_minify = mocker.MagicMock(return_value=0) +# mocker.patch("stubber.commands.minify_cmd.minify", mock_minify) - result = runner.invoke(stubber.stubber_cli, ["minify"]) - assert result.exit_code == 0 - mock_minify.assert_called_once() +# result = runner.invoke(stubber.stubber_cli, ["minify"]) +# assert result.exit_code == 0 +# mock_minify.assert_called_once() -@pytest.mark.mocked -def test_cmd_minify_all(mocker: MockerFixture): - # check basic command line sanity check - runner = CliRunner() - mock_minify: MagicMock = mocker.MagicMock(return_value=0) - mocker.patch("stubber.commands.minify_cmd.minify", mock_minify) +# @pytest.mark.mocked +# def test_cmd_minify_all(mocker: MockerFixture): +# # check basic command line sanity check +# runner = CliRunner() +# mock_minify = mocker.MagicMock(return_value=0) +# mocker.patch("stubber.commands.minify_cmd.minify", mock_minify) - result = runner.invoke(stubber.stubber_cli, ["minify", "--all"]) - assert result.exit_code == 0 - assert mock_minify.call_count == 3 +# result = runner.invoke(stubber.stubber_cli, ["minify", "--all"]) +# assert result.exit_code == 0 +# assert mock_minify.call_count == 3 ########################################################################################## @@ -228,9 +203,9 @@ def test_cmd_minify_all(mocker: MockerFixture): def test_cmd_stub(mocker: MockerFixture): # check basic command line sanity check runner = CliRunner() - # m_generate: MagicMock = mocker.patch("stubber.commands.stub_cmd.generate_pyi_files", autospec=True, return_value=True) - m_generate: MagicMock = mocker.MagicMock(return_value=True) - m_postprocessing: MagicMock = mocker.MagicMock() + # m_generate = mocker.patch("stubber.commands.stub_cmd.generate_pyi_files", autospec=True, return_value=True) + m_generate = mocker.MagicMock(return_value=True) + m_postprocessing = mocker.MagicMock() mocker.patch("stubber.commands.stub_cmd.generate_pyi_files", m_generate) mocker.patch("stubber.commands.stub_cmd.do_post_processing", m_postprocessing) # fake run on current folder @@ -238,9 +213,7 @@ def test_cmd_stub(mocker: MockerFixture): m_generate.assert_called_once_with(Path(".")) m_postprocessing.assert_called_once() - m_postprocessing.assert_called_once_with( - [Path(".")], stubgen=False, black=True, autoflake=False - ) + m_postprocessing.assert_called_once_with([Path(".")], stubgen=False, black=True, autoflake=False) assert result.exit_code == 0 @@ -252,58 +225,51 @@ def test_cmd_get_frozen(mocker: MockerFixture, tmp_path: Path): # check basic command line sanity check runner = CliRunner() - m_get_local_tag: MagicMock = mocker.patch( - "stubber.basicgit.get_local_tag", autospec=True, return_value="v1.42" - ) + m_get_local_tag = mocker.patch("stubber.basicgit.get_local_tag", autospec=True, return_value="v1.42") - m_freeze_any: MagicMock = mocker.patch( - "stubber.commands.get_frozen_cmd.freeze_any", autospec=True - ) - m_post: MagicMock = mocker.patch("stubber.utils.do_post_processing", autospec=True) + m_freeze_any = mocker.patch("stubber.commands.get_frozen_cmd.freeze_any", autospec=True) + m_post = mocker.patch("stubber.utils.do_post_processing", autospec=True) # fake run - need to ensure that there is a destination folder - result = runner.invoke( - stubber.stubber_cli, ["get-frozen", "--stub-folder", tmp_path.as_posix()] - ) + result = runner.invoke(stubber.stubber_cli, ["get-frozen", "--stub-folder", tmp_path.as_posix()]) assert result.exit_code == 0 # FIXME : test fails in CI m_freeze_any.assert_called_once() m_get_local_tag.assert_called_once() - m_post.assert_any_call( - [tmp_path / "micropython-v1_42-frozen"], - stubgen=True, - black=False, - autoflake=False, - ) - m_post.assert_any_call( - [tmp_path / "micropython-v1_42-frozen"], - stubgen=False, - black=True, - autoflake=True, - ) - - -########################################################################################## -# get-lobo -########################################################################################## -@pytest.mark.mocked -def test_cmd_get_lobo(mocker: MockerFixture, tmp_path: Path): - # check basic command line sanity check - runner = CliRunner() - - mock_get_frozen: MagicMock = mocker.patch("stubber.get_lobo.get_frozen", autospec=True) - mock_post: MagicMock = mocker.patch("stubber.utils.do_post_processing", autospec=True) - - # fake run - result = runner.invoke(stubber.stubber_cli, ["get-lobo", "--stub-folder", tmp_path.as_posix()]) - assert result.exit_code == 0 - mock_get_frozen.assert_called_once() - mock_post.assert_called_once() - mock_post.assert_called_once_with( - [tmp_path / "loboris-v3_2_24-frozen"], stubgen=True, black=True, autoflake=True - ) - assert result.exit_code == 0 + assert m_post.call_count == 2 + # m_post.assert_any_call( + # [tmp_path / "micropython-v1_42-frozen"], + # stubgen=True, + # black=False, + # autoflake=False, + # ) + # m_post.assert_any_call( + # [tmp_path / "micropython-v1_42-frozen"], + # stubgen=False, + # black=True, + # autoflake=True, + # ) + + +# ########################################################################################## +# # get-lobo +# ########################################################################################## +# @pytest.mark.mocked +# def test_cmd_get_lobo(mocker: MockerFixture, tmp_path: Path): +# # check basic command line sanity check +# runner = CliRunner() + +# mock_get_frozen = mocker.patch("stubber.get_lobo.get_frozen", autospec=True) +# mock_post = mocker.patch("stubber.utils.do_post_processing", autospec=True) + +# # fake run +# result = runner.invoke(stubber.stubber_cli, ["get-lobo", "--stub-folder", tmp_path.as_posix()]) +# assert result.exit_code == 0 +# mock_get_frozen.assert_called_once() +# mock_post.assert_called_once() +# mock_post.assert_called_once_with([tmp_path / "loboris-v3_2_24-frozen"], stubgen=True, black=True, autoflake=True) +# assert result.exit_code == 0 ########################################################################################## @@ -313,8 +279,8 @@ def test_cmd_get_lobo(mocker: MockerFixture, tmp_path: Path): def test_cmd_get_core(mocker: MockerFixture, tmp_path: Path): # check basic command line sanity check runner = CliRunner() - mock: MagicMock = mocker.patch("stubber.get_cpython.get_core", autospec=True) - mock_post: MagicMock = mocker.patch("stubber.utils.do_post_processing", autospec=True) + mock = mocker.patch("stubber.get_cpython.get_core", autospec=True) + mock_post = mocker.patch("stubber.utils.do_post_processing", autospec=True) # fake run result = runner.invoke(stubber.stubber_cli, ["get-core", "--stub-folder", tmp_path.as_posix()]) @@ -339,21 +305,15 @@ def test_cmd_get_docstubs(mocker: MockerFixture, tmp_path: Path): # check basic command line sanity check runner = CliRunner() - m_get_l_tag: MagicMock = mocker.patch( - "stubber.basicgit.get_local_tag", autospec=True, return_value="v1.42" - ) + m_get_l_tag = mocker.patch("stubber.basicgit.get_local_tag", autospec=True, return_value="v1.42") # from stubber.commands.get_docstubs import generate_from_rst - mock: MagicMock = mocker.patch( - "stubber.commands.get_docstubs_cmd.generate_from_rst", autospec=True - ) + mock = mocker.patch("stubber.commands.get_docstubs_cmd.generate_from_rst", autospec=True) - mock_post: MagicMock = mocker.patch("stubber.utils.do_post_processing", autospec=True) + mock_post = mocker.patch("stubber.utils.do_post_processing", autospec=True) # fake run - result = runner.invoke( - stubber.stubber_cli, ["get-docstubs", "--stub-folder", tmp_path.as_posix()] - ) + result = runner.invoke(stubber.stubber_cli, ["get-docstubs", "--stub-folder", tmp_path.as_posix()]) assert result.exit_code == 0 # process is called twice assert mock.call_count == 1 @@ -362,24 +322,20 @@ def test_cmd_get_docstubs(mocker: MockerFixture, tmp_path: Path): ########################################################################################## -# get-lobo +# fallback ########################################################################################## -@pytest.mark.mocked -def test_cmd_fallback(mocker: MockerFixture, tmp_path: Path): - # check basic command line sanity check - runner = CliRunner() +# @pytest.mark.mocked +# def test_cmd_fallback(mocker: MockerFixture, tmp_path: Path): +# # check basic command line sanity check +# runner = CliRunner() - mock: MagicMock = mocker.patch( - "stubber.commands.upd_fallback_cmd.update_fallback", autospec=True - ) - # mock2: MagicMock = mocker.patch("stubber.update_fallback.update_fallback", autospec=True) - # from .update_fallback import update_fallback, - # fake run - result = runner.invoke( - stubber.stubber_cli, ["update-fallback", "--stub-folder", tmp_path.as_posix()] - ) - mock.assert_called_once() - assert result.exit_code == 0 +# mock = mocker.patch("stubber.commands.upd_fallback_cmd.update_fallback", autospec=True) +# # mock2 = mocker.patch("stubber.update_fallback.update_fallback", autospec=True) +# # from .update_fallback import update_fallback, +# # fake run +# result = runner.invoke(stubber.stubber_cli, ["update-fallback", "--stub-folder", tmp_path.as_posix()]) +# mock.assert_called_once() +# assert result.exit_code == 0 ########################################################################################## @@ -396,9 +352,7 @@ def test_cmd_fallback(mocker: MockerFixture, tmp_path: Path): def test_cmd_merge(mocker: MockerFixture, cmdline: List[str]): runner = CliRunner() # from stubber.commands.clone import git - m_merge_docstubs: MagicMock = mocker.patch( - "stubber.commands.merge_cmd.merge_all_docstubs", autospec=True, return_value={} - ) + m_merge_docstubs = mocker.patch("stubber.commands.merge_cmd.merge_all_docstubs", autospec=True, return_value={}) result = runner.invoke(stubber.stubber_cli, cmdline) assert result.exit_code == 0 m_merge_docstubs.assert_called_once() @@ -418,9 +372,7 @@ def test_cmd_merge(mocker: MockerFixture, cmdline: List[str]): def test_cmd_publish(mocker: MockerFixture, cmdline: List[str]): runner = CliRunner() # from stubber.commands.clone import git - m_publish_multiple: MagicMock = mocker.patch( - "stubber.commands.publish_cmd.publish_multiple", autospec=True, return_value={} - ) + m_publish_multiple = mocker.patch("stubber.commands.publish_cmd.publish_multiple", autospec=True, return_value={}) result = runner.invoke(stubber.stubber_cli, cmdline) assert result.exit_code == 0 m_publish_multiple.assert_called_once() diff --git a/tests/common/upd_fallback_test.py b/tests/common/upd_fallback_test.py index 78e642c09..b8bd595f6 100644 --- a/tests/common/upd_fallback_test.py +++ b/tests/common/upd_fallback_test.py @@ -1,6 +1,7 @@ import os from pathlib import Path +import pytest # pylint: disable=wrong-import-position,import-error # Module Under Test @@ -8,6 +9,7 @@ from stubber.utils.config import CONFIG +@pytest.mark.skip("deprecated") def test_update_fallback(tmp_path): # test requires an actuall filled source # from actual source @@ -21,6 +23,7 @@ def test_update_fallback(tmp_path): assert count >= 0 +@pytest.mark.skip("deprecated") def test_update_fallback_2(tmp_path: Path): # test requires an actuall filled source # from actual source diff --git a/tests/createstubs/createstubs_all_test.py b/tests/createstubs/createstubs_all_test.py index c4454219c..b48c91a84 100644 --- a/tests/createstubs/createstubs_all_test.py +++ b/tests/createstubs/createstubs_all_test.py @@ -4,7 +4,7 @@ from collections import namedtuple from importlib import import_module from pathlib import Path -from typing import Any, Dict, Generator, List +from typing import Any, Dict, Generator, List, NamedTuple, Optional import pytest from mock import MagicMock @@ -42,24 +42,6 @@ def test_firmwarestubber_all_versions_same( assert FIRST_VERSION == parse(createstubs.__version__) -@pytest.mark.parametrize("variant", VARIANTS) -@pytest.mark.parametrize("location", LOCATIONS) -@pytest.mark.skip(reason="not sure if this is needed") -def test_firmwarestubber_base_version_match_package( - location: Any, - variant: str, - mock_micropython_path: Generator[str, None, None], -): - # Q&D Location - path = Path(__file__).resolve().parents[2] / "pyproject.toml" - pyproject = tomllib.loads(open(str(path)).read()) - pyproject_version = pyproject["tool"]["poetry"]["version"] - - createstubs = import_variant(location, variant) - # base version should match the package - assert parse(createstubs.__version__).base_version == parse(pyproject_version).base_version - - @pytest.mark.parametrize("variant", VARIANTS) @pytest.mark.parametrize("location", LOCATIONS) def test_stubber_Class_available( @@ -74,9 +56,7 @@ def test_stubber_Class_available( @pytest.mark.parametrize("variant", VARIANTS) @pytest.mark.parametrize("location", LOCATIONS) -def test_stubber_info_basic( - location: Any, variant: str, mock_micropython_path: Generator[str, None, None] -): +def test_stubber_info_basic(location: Any, variant: str, mock_micropython_path: Generator[str, None, None]): createstubs = import_variant(location, variant) stubber = createstubs.Stubber() assert stubber is not None, "Can't create Stubber instance" @@ -114,26 +94,24 @@ def test_stubber_info_custom( ################################################# # test the fwid naming on the different platforms ################################################# -from testcases import fwid_test_cases +from testcases import MP_Implementation, fwid_test_cases # @pytest.mark.parametrize("variant", VARIANTS) # @pytest.mark.parametrize("location", LOCATIONS) @pytest.mark.parametrize( - "fwid, sys_imp_name, sys_imp_version, sys_platform, os_uname, mock_modules", + "fwid, sys_implementation, sys_platform, sys_version, os_uname, mock_modules", fwid_test_cases, ids=[e[0] for e in fwid_test_cases], ) @pytest.mark.mocked def test_stubber_fwid( mock_micropython_path: Generator[str, None, None], - # location: str, - # variant: str, mocker: MockerFixture, fwid: str, - sys_imp_name: str, - sys_imp_version: tuple, + sys_implementation: MP_Implementation, sys_platform: str, + sys_version: str, os_uname: Dict, mock_modules: List[str], ): @@ -141,19 +119,28 @@ def test_stubber_fwid( location = "board" createstubs = import_variant(location, variant) - # FIX-ME : This does not yet cover minified mod_name = f"stubber.board.{variant}" # class.property : just pass a value mocker.patch(f"{mod_name}.sys.platform", sys_platform) - mocker.patch(f"{mod_name}.sys.implementation.name", sys_imp_name) - mocker.patch(f"{mod_name}.sys.implementation.version", sys_imp_version) + # fatch sys.implementation + mocker.patch(f"{mod_name}.sys.implementation.name", sys_implementation.name) + mocker.patch(f"{mod_name}.sys.implementation.version", sys_implementation.version) + mocker.patch(f"{mod_name}.sys.version", sys_version) + if sys_implementation._machine: + mocker.patch(f"{mod_name}.sys.implementation._machine", sys_implementation._machine, create=True) + if sys_implementation._mpy: + mocker.patch(f"{mod_name}.sys.implementation._mpy", sys_implementation._mpy, create=True) # class.method--> mock using function - fake_uname = os_uname - def mock_uname(): - return fake_uname + if os_uname: + # only mock uname if there is something to mock + fake_uname = os_uname + + def mock_uname(): + return fake_uname + + mocker.patch(f"{mod_name}.os.uname", mock_uname, create=True) - mocker.patch(f"{mod_name}.os.uname", mock_uname, create=True) for mod in mock_modules: # mock that these modules can be imported without errors sys.modules[mod] = MagicMock() @@ -182,8 +169,7 @@ def mock_uname(): assert Version(info["version"]), "provided version is not a valid version" assert info["port"] != "", "stubber.info() - No port detected" - assert info["board"] != "", "stubber.info() - No board detected" - + # TEST 2: check if the firmware id is correct new_fwid = stubber._fwid assert new_fwid != "none" @@ -192,30 +178,15 @@ def mock_uname(): assert c not in stubber.flat_fwid, "flat_fwid must not contain '{}'".format(c) # Does the firmware id match (at least the part before the last -) - assert new_fwid.startswith( - fwid.rsplit("-", 1)[0] - ), f"fwid: {new_fwid} does not start with {fwid.rsplit('-', 1)[0]}" + + short_fwid = "-".join(fwid.split("-", 2)[:2]) + assert new_fwid.startswith(short_fwid), f"fwid: {new_fwid} does not start with {short_fwid}" if not "esp8266" in fwid: # TODO: Fix FWID logic with esp8266 assert new_fwid == fwid, f"fwid: {new_fwid} does not match" -# # throws an error on the commandline -# @pytest.mark.skip(reason="test not working") -# @pytest.mark.parametrize("variant", VARIANTS) -# @pytest.mark.parametrize("location", LOCATIONS) -# def test_read_path( -# location, -# variant, -# mock_micropython_path, -# ): -# # import createstubs # type: ignore -# createstubs = import_module(f"{location}.{variant}") # type: ignore - -# assert createstubs.read_path() == "" - - @pytest.mark.parametrize("variant", VARIANTS) @pytest.mark.parametrize("location", LOCATIONS) def test_create_all_stubs( @@ -234,9 +205,8 @@ def test_create_all_stubs( stubber.add_modules(["http_client", "webrepl", "_internal"]) stubber.create_all_stubs() - stublist = list(tmp_path.glob("**/*.py")) + stublist = list(tmp_path.glob("**/*.pyi")) assert len(stublist) == 3 - stubber.report() stublist = list(tmp_path.glob("**/modules.json")) assert len(stublist) == 1 @@ -272,11 +242,12 @@ def test_create_module_stub( myid = "MyCustomID" stubber = createstubs.Stubber(path=str(tmp_path), firmware_id=myid) # type: ignore assert stubber is not None, "Can't create Stubber instance" + stubber.report_start() # just in the test folder , no structure - stubber.create_module_stub("json", str(tmp_path / "json.py")) - stubber.create_module_stub("_thread", str(tmp_path / "_thread.py")) + stubber.create_module_stub("json", str(tmp_path / "json.pyi")) + stubber.create_module_stub("_thread", str(tmp_path / "_thread.pyi")) - stublist = list(tmp_path.glob("**/*.py")) + stublist = list(tmp_path.glob("**/*.pyi")) assert len(stublist) == 2 @@ -293,9 +264,9 @@ def test_create_module_stub_folder( myid = "MyCustomID" stubber = createstubs.Stubber(path=str(tmp_path), firmware_id=myid) # type: ignore assert stubber is not None, "Can't create Stubber instance" - + stubber.report_start() stubber.create_module_stub("json") - stublist = list((tmp_path / "stubs" / myid.lower()).glob("**/*.py")) + stublist = list((tmp_path / "stubs" / myid.lower()).glob("**/*.pyi")) assert len(stublist) == 1, "should create stub in stub folder if no folder specified" @@ -334,9 +305,10 @@ def test_nested_modules( myid = "MyCustomID" stubber = createstubs.Stubber(path=str(tmp_path), firmware_id=myid) # type: ignore assert stubber is not None, "Can't create Stubber instance" + stubber.report_start() # just in the test folder , no structure - stubber.create_module_stub("urllib/request", str(tmp_path / "request.py")) - stublist = list(tmp_path.glob("**/*.py")) + stubber.create_module_stub("urllib/request", str(tmp_path / "request.pyi")) + stublist = list(tmp_path.glob("**/*.pyi")) assert len(stublist) == 1 @@ -353,24 +325,32 @@ def test_unavailable_modules( myid = "MyCustomID" stubber = createstubs.Stubber(path=str(tmp_path), firmware_id=myid) # type: ignore assert stubber is not None, "Can't create Stubber instance" + stubber.report_start() # this should not generate a module , but also should not th - stubber.create_module_stub("notamodule1", str(tmp_path / "notamodule1.py")) - stubber.create_module_stub("not/amodule2", str(tmp_path / "notamodule2.py")) - stublist = list(tmp_path.glob("**/*.py")) + stubber.create_module_stub("notamodule1", str(tmp_path / "notamodule1.pyi")) + stubber.create_module_stub("not/amodule2", str(tmp_path / "notamodule2.pyi")) + stublist = list(tmp_path.glob("**/*.pyi")) assert len(stublist) == 0 -# def test_clean(tmp_path): -# import createstubs # type: ignore -# createstubs = import_module(f"{location}.{variant}") # type: ignore -# myid = "MyCustomID" -# test_path = str(tmp_path) -# stub_path = Path(test_path) /"stubs"/ myid.lower() -# stubber = Stubber(path = test_path, firmware_id=myid) -# stubber.clean() - -# #Create a file -# stubber.create_module_stub("json", PurePosixPath( stub_path / "json.py") ) -# stublist = list(Path(test_path).glob('**/*.py')) -# assert len(stublist) == 1 -# stubber.clean() +@pytest.mark.parametrize( + "input, expected", + [ + ("", ""), + ("v1.13 on 2020-10-09", ""), + ("v1.13-103-gb137d064e on 2020-10-09", "103"), + ("3.4.0; MicroPython v1.23.0-preview.6.g3d0b6276f on 2024-01-02", "6"), + ("3.4.0; MicroPython v1.22.0 on 2023-12-27", ""), + ], +) +def test_build(input: str, expected: str): + """build function should be able to extract from + - sys.version + - sys.implementation.version + """ + variant = "createstubs" + location = "board" + createstubs = import_variant(location, variant) + + outcome = createstubs._build(input) + assert outcome == expected diff --git a/tests/createstubs/literal_test.py b/tests/createstubs/literal_test.py index 89ac033ba..3558d1323 100644 --- a/tests/createstubs/literal_test.py +++ b/tests/createstubs/literal_test.py @@ -94,7 +94,7 @@ def test_literal_init_order( # check literals defined before first method class_line = lines.index("class SoftSPI():") - LSB_line = lines.index(" LSB = 1 # type: int", class_line + 1) + LSB_line = lines.index(" LSB: int = 1", class_line + 1) init_line = lines.index(" def __init__(self, *argv, **kwargs) -> None:", class_line + 1) assert class_line < LSB_line < init_line, "Literals MUST be listed before class methods" diff --git a/tests/createstubs/shared.py b/tests/createstubs/shared.py index 6dc7d7a44..f077bcad1 100644 --- a/tests/createstubs/shared.py +++ b/tests/createstubs/shared.py @@ -1,6 +1,7 @@ -import pytest from importlib import import_module +import pytest + LOCATIONS = ["board", pytest.param("minified", marks=pytest.mark.minified)] VARIANTS = ["createstubs", "createstubs_mem", "createstubs_db"] diff --git a/tests/createstubs/testcases.py b/tests/createstubs/testcases.py index 2d1f5130f..439c83b32 100644 --- a/tests/createstubs/testcases.py +++ b/tests/createstubs/testcases.py @@ -4,6 +4,7 @@ from collections import namedtuple +from typing import Any, Optional, Tuple ## os.uname() # import os; print(os.uname()) @@ -77,7 +78,13 @@ version="v1.17 on 2021-09-02", machine="ESP module with ESP8266", ) - +uname_mpy_v1_22_esp32_GEN = UName( + sysname="esp32", + nodename="esp32", + release="1.22.0", + version="v1.22.0 on 2023-12-27", + machine="Generic ESP32 module with SPIRAM with ESP32", +) pyb1_v1_13_PYB11 = UName( sysname="pyboard", @@ -98,104 +105,138 @@ # TODO: add support for -Latest # ('micropython-1.13-latest-esp32', 'micropython', 'esp32', mpy_v1_13_build), + + +class MP_Implementation: + """Mock sys.implementation for MicroPython""" + + name: str + version: Tuple + _machine: Optional[str] # newer MicroPython versions have this attribute + _mpy: Optional[int] # MicroPython >= 1.11 + + # Define __getattr__, as the documentation states: + # > sys.implementation may contain additional attributes specific to the Python implementation. + # > These non-standard attributes must start with an underscore, and are not described here. + def __getattr__(self, name: str) -> Any: + ... + + def __init__(self, name: str, version: Tuple, *, _machine=None, _mpy=None): + self.name = name + self.version = version + self._machine = _machine + self._mpy = _mpy + + fwid_test_cases = [ # - expected firmware id - # - patch input - sys.implementation.name - # - patch input - sys.implementation.version + # - patch input - sys.implementation( name, version, _machine, _mpy) # - patch input - sys.platform - # - patch input - sys.os.uname stucture + # - patch input - sys.version (str) + # - patch input - os.uname stucture # - patch input - array of modules to mock for firmware detection - # mpy esp32 - ("micropython-v1.9.4-esp32-GENERIC", "micropython", (1, 9, 4), "esp32", mpy_v1_9_4, []), - ("micropython-v1.10-esp32-GENERIC", "micropython", (1, 10, 0), "esp32", mpy_v1_10, []), - # ("micropython-v1.13-103-esp32-GENERIC_SPIRAM", "micropython", (1, 13, 0), "esp32", mpy_v1_13_build, []), - ( - "micropython-v1.13-103-esp32-GENERIC", - "micropython", - (1, 13, 0), + ( + "micropython-v1.9.4-esp32", + MP_Implementation(name="micropython", version=(1, 9, 4)), "esp32", + "", + mpy_v1_9_4, + [], + ), + ( + "micropython-v1.10-esp32", + MP_Implementation( + "micropython", + (1, 10, 0), + ), + "esp32", + "", + mpy_v1_10, + [], + ), + ( + "micropython-v1.13-preview-esp32", + MP_Implementation( + "micropython", + (1, 13, 0), + ), + "esp32", + "3.4.0", mpy_v1_13_build, [], ), - # mpy esp8266 ( - "micropython-v1.11-esp8266-GENERIC", - "micropython", - (1, 11, 0), + "micropython-v1.11-esp8266", + MP_Implementation( + "micropython", + (1, 11, 0), + ), "esp8266", + "", mpy_v1_11_esp8266, [], ), ( - "micropython-v1.11-8-esp8266-GENERIC", - "micropython", - (1, 11, 0), + "micropython-v1.11-preview-esp8266", + MP_Implementation( + "micropython", + (1, 11, 0), + ), "esp8266", + "", mpy_v1_11_8_esp8266, [], ), ( - "micropython-v1.17-esp8266-GENERIC", - "micropython", - (1, 17, 0), + "micropython-v1.17-esp8266", + MP_Implementation( + "micropython", + (1, 17, 0), + ), "esp8266", + "", mpy_v1_17_esp8266_GEN, [], ), # mpy pyb1 - ("micropython-v1.13-95-stm32-PYBV11", "micropython", (1, 13, 0), "pyb1", pyb1_v1_13_PYB11, []), - # RP2 ( - "micropython-v1.18-rp2-RPI_PICO", - "micropython", - (1, 18, 0), - "rp2", - UName( - sysname="rp2", - nodename="rp2", - release="1.18.0", - version="v1.18-g0fff2e03f on 2020-10-03", - machine="Raspberry Pi Pico with RP2040", + "micropython-v1.13-preview-stm32", + MP_Implementation( + "micropython", + (1, 13, 0), ), + "pyb1", + "", + pyb1_v1_13_PYB11, [], ), + # RP2 ( - "micropython-v1.19.1-721-rp2-RPI_PICO_W", - "micropython", - (1, 19, 1), - "rp2", - UName( - sysname="rp2", - nodename="rp2", - release="1.19.1", - version="v1.19.1-721-gd5181034f on 2022-11-28", - machine="Raspberry Pi Pico W with RP2040", + "micropython-v1.18-preview-rp2", + MP_Implementation( + "micropython", + (1, 18, 0), ), - [], - ), - ( - "micropython-v1.19.1-rp2-PIMORONI_PICOLIPO_16MB", - "micropython", - (1, 19, 1), "rp2", + "", UName( sysname="rp2", nodename="rp2", - release="1.19.1", - version="67fac4e on 2023-02-16 (GNU 9.2.1 MinSizeRel)", - machine="Pimoroni Pico LiPo 16MB with RP2040", + release="1.18.0", + version="v1.18-g0fff2e03f on 2020-10-03", + machine="Raspberry Pi Pico with RP2040", ), [], ), - # lobo - # ("loboris-v3.2.24-esp32", "micropython", (3,2, 24), "esp32_LoBo", lobo, []), - # ("loboris-v3.2.24-esp32", "micropython", (3,2, 24), "esp32_LoBo", lobo_bt_ram, []), # ev3_pybricks_1_0_0 ( "ev3-pybricks-v2.0.0-unix", - "", - (2, 0, 0), + MP_Implementation( + "", + (2, 0, 0), + ), "linux", + "", UName( machine="ev3", nodename="ev3", @@ -217,10 +258,13 @@ # ), # pycopy ( - "pycopy-v1.2.3-beta0-stm32", - "pycopy", - (1, 2, 3), + "pycopy-v1.2.3-stm32", + MP_Implementation( + "pycopy", + (1, 2, 3), + ), "pyb1", + "", UName( sysname="stm32", nodename="stm32", @@ -232,10 +276,13 @@ ), # pycom ( - "pycom-v1.18.0-95-esp32", - "pycom", - (1, 18, 0), + "pycom-v1.18.0-preview-esp32", + MP_Implementation( + "pycom", + (1, 18, 0), + ), "esp32", + "", UName( sysname="esp32", nodename="esp32", @@ -245,4 +292,70 @@ ), ["pycom"], ), + # 1.19.1 + ( + "micropython-v1.19.1-preview-rp2", + MP_Implementation("micropython", (1, 19, 1)), + "rp2", + "", + UName( + sysname="rp2", + nodename="rp2", + release="1.19.1", + version="v1.19.1-721-gd5181034f on 2022-11-28", + machine="Raspberry Pi Pico W with RP2040", + ), + [], + ), + ( + "micropython-v1.19.1-rp2", + MP_Implementation("micropython", (1, 19, 1)), + "rp2", + "", + UName( + sysname="rp2", + nodename="rp2", + release="1.19.1", + version="67fac4e on 2023-02-16 (GNU 9.2.1 MinSizeRel)", + machine="Pimoroni Pico LiPo 16MB with RP2040", + ), + [], + ), + # version 1.22 + ( + # using uname (old) + "micropython-v1.22.0-esp32", + MP_Implementation("micropython", (1, 22, 0, "")), + "esp32", + "", + uname_mpy_v1_22_esp32_GEN, + [], + ), + ( + # using _machine and uname + "micropython-v1.22.0-esp32", + MP_Implementation("micropython", (1, 22, 0, ""), _machine="Generic ESP32 module with SPIRAM with ESP32"), + "esp32", + "", + uname_mpy_v1_22_esp32_GEN, + [], + ), + ( + # using _machine only (new) + "micropython-v1.22.0-samd", + MP_Implementation("micropython", (1, 22, 0, ""), _machine="Wio Terminal D51R with SAMD51P19A"), + "samd", + "", + None, + [], + ), + ( + # using _machine only (new) + "micropython-v1.23.0-preview-samd", + MP_Implementation("micropython", (1, 23, 0, "preview"), _machine="Wio Terminal D51R with SAMD51P19A"), + "samd", + "3.4.0; MicroPython v1.23.0-preview.6.g3d0b6276f on 2024-01-02", # sys.version + None, + [], + ), ] diff --git a/tests/freeze/freezer_mpy_test.py b/tests/freeze/freezer_mpy_test.py index 26f22fb7d..78f4a2c72 100644 --- a/tests/freeze/freezer_mpy_test.py +++ b/tests/freeze/freezer_mpy_test.py @@ -66,9 +66,7 @@ def test_get_portboard(path: str, port: str, board: str): assert _port == port -def test_manifest_uasync( - tmp_path: Path, testrepo_micropython: Path, testrepo_micropython_lib: Path -): +def test_manifest_uasync(tmp_path: Path, testrepo_micropython: Path, testrepo_micropython_lib: Path): "test if task.py is included with the uasyncio frozen module" mpy_version = "v1.18" mpy_folder = testrepo_micropython.absolute() @@ -80,9 +78,7 @@ def test_manifest_uasync( manifest = mpy_folder / "ports/esp32/boards/manifest.py" freeze_one_manifest_2(manifest, stub_folder, mpy_folder, lib_folder, mpy_version) - assert ( - tmp_path / "esp32" / GENERIC_U / "uasyncio/task.py" - ).exists(), "task.py must be included in uasyncio" + assert (tmp_path / "esp32" / GENERIC_U / "uasyncio/task.py").exists(), "task.py must be included in uasyncio" ####################################################################################################################### @@ -264,11 +260,9 @@ def test_freeze_any_mocked( ): "mocked test if we can freeze source using manifest.py files" - m_freeze_folders: MagicMock = mocker.patch( - "stubber.freeze.get_frozen.freeze_folders", autospec=True, return_value=[1] - ) - # m_freeze_one_manifest_1: MagicMock = mocker.patch("stubber.freeze.get_frozen.freeze_one_manifest_1", autospec=True, return_value=1) - m_freeze_one_manifest_2: MagicMock = mocker.patch( + m_freeze_folders = mocker.patch("stubber.freeze.get_frozen.freeze_folders", autospec=True, return_value=[1]) + # m_freeze_one_manifest_1= mocker.patch("stubber.freeze.get_frozen.freeze_one_manifest_1", autospec=True, return_value=1) + m_freeze_one_manifest_2 = mocker.patch( "stubber.freeze.get_frozen.freeze_one_manifest_2", autospec=True, return_value=1 ) x = freeze_any( @@ -297,25 +291,22 @@ def test_freeze_manifest2_error_mocked( ): "mocked test if we can freeze source using manifest.py files" - m_freeze_folders: MagicMock = mocker.patch( - "stubber.freeze.get_frozen.freeze_folders", autospec=True, return_value=[1] - ) - # m_freeze_one_manifest_1: MagicMock = mocker.patch("stubber.freeze.get_frozen.freeze_one_manifest_1", autospec=True, return_value=1) - m_freeze_one_manifest_2: MagicMock = mocker.patch( + m_freeze_folders = mocker.patch("stubber.freeze.get_frozen.freeze_folders", autospec=True, return_value=[1]) + m_freeze_one_manifest_2 = mocker.patch( "stubber.freeze.get_frozen.freeze_one_manifest_2", autospec=True, return_value=1 ) # get the correct version to test switch(mpy_version, mpy_path=testrepo_micropython, mpy_lib_path=testrepo_micropython_lib) - x = freeze_any( + test_path = freeze_any( tmp_path, version=mpy_version, mpy_path=testrepo_micropython, mpy_lib_path=testrepo_micropython_lib, ) - assert x >= 1, "expect >= 1 stubs" + assert test_path is not None, "expect a path" + # no further asserts of path as this is mocked assert m_freeze_folders.call_count == 0, "expect no calls to freeze_folders" assert m_freeze_one_manifest_2.call_count == 34, "34 calls to freeze_one_manifest_2" - # assert m_freeze_one_manifest_1.call_count == 0 ########################################################################## diff --git a/tests/minify/test_minify_script.py b/tests/minify/test_minify_script.py index 3ebcc7886..30b971bde 100644 --- a/tests/minify/test_minify_script.py +++ b/tests/minify/test_minify_script.py @@ -64,9 +64,7 @@ def test_get_whitespace_context(content, index, expected): # Happy path tests ( ["#0", "__version__ = 1", "print('Hello, World!')", ""], - # ["#0", "__version__ = 1", "# print('Hello, World!')", ""], - ["__version__=1"], - # "\n".join(["__version__=1"]), + ["__version__=1", "print('Hello, World!')"], True, ), ( @@ -100,5 +98,6 @@ def test_minify_script(source_script, expected, keep_report): result = minify_script(source, keep_report, diff=True) result = result.split("\n") + assert len(result) == len(expected), "Lengths do not match" for i, line in enumerate(result): assert expected[i] == line diff --git a/tests/native/native_createstubs_test.py b/tests/native/native_createstubs_test.py index c9c851f04..640783454 100644 --- a/tests/native/native_createstubs_test.py +++ b/tests/native/native_createstubs_test.py @@ -65,9 +65,7 @@ "firmware", fw_list, ) -def test_createstubs( - firmware: str, variant: str, suffix: str, tmp_path: Path, pytestconfig: Config -): +def test_createstubs(firmware: str, variant: str, suffix: str, tmp_path: Path, pytestconfig: Config): "run createstubs in the native (linux/windows) version of micropython" # all createstub variants are in the same folder script_path = (pytestconfig.rootpath / "src" / "stubber" / "board").absolute() @@ -106,7 +104,7 @@ def test_createstubs( # did it run without error ? stub_path = tmp_path / "stubs" - stubfiles = list(stub_path.rglob("*.py")) + stubfiles = list(stub_path.rglob("*.pyi")) # manifest exists jsons = list(stub_path.rglob("modules.json")) @@ -121,9 +119,7 @@ def test_createstubs( for x in ["firmware", "stubber", "modules"]: assert x in manifest.keys(), "module manifest should contain firmware, stubber , modules" - assert ( - len(manifest["modules"]) - len(stubfiles) == 0 - ), "number of modules must match count of stubfiles." + assert len(manifest["modules"]) - len(stubfiles) == 0, "number of modules must match count of stubfiles." # Delete databaseafter the test if variant == "createstubs_db": (script_path / "modulelist.done").unlink(missing_ok=False) # MUST exist diff --git a/tests/publish/test_candidates.py b/tests/publish/test_candidates.py index 21e94926d..b052ef183 100644 --- a/tests/publish/test_candidates.py +++ b/tests/publish/test_candidates.py @@ -1,4 +1,5 @@ """Test candidates.py""" + from pathlib import Path from typing import Generator, List, Union @@ -74,7 +75,7 @@ def test_docstub_candidates(pytestconfig, family, versions, count): # list GENERIC boards for any version (case sensitive on linux/mac) ("micropython", "auto", "auto", "GENERIC", 11), ("micropython", "auto", "auto", "generic", 11), - ("micropython", "latest", "auto", "auto", 7), + ("micropython", "preview", "auto", "auto", 7), ("micropython", "v1.16", "foo", "GENERIC", 0), # port folder does not exist ("micropython", "v1.16", "foo", "generic", 0), # port folder does not exist # list GENERIC boards for specific version (case sensitive on linux/mac) @@ -90,12 +91,12 @@ def test_docstub_candidates(pytestconfig, family, versions, count): ("micropython", "v1.18", "stm32", "pybd_sf2", 2), # Self + Generic ], ) +@pytest.mark.xfail() +# "test is overdepedent on the data in the stubber-repo" def test_frozen_candidates(pytestconfig, family, versions, ports, boards, count): # test data path = pytestconfig.rootpath / "tests/publish/data/stub-version" - frozen = frozen_candidates( - path=path, family=family, versions=versions, ports=ports, boards=boards - ) + frozen = frozen_candidates(path=path, family=family, versions=versions, ports=ports, boards=boards) assert isinstance(frozen, Generator) l = list(frozen) print() @@ -119,9 +120,7 @@ def test_frozen_candidates_err(pytestconfig, family, versions, ports, boards, co # test data path = pytestconfig.rootpath / "tests/publish/data/stub-version" with pytest.raises(Exception) as exc_info: - _ = frozen_candidates( - path=path, family=family, versions=versions, ports=ports, boards=boards - ) + _ = frozen_candidates(path=path, family=family, versions=versions, ports=ports, boards=boards) assert exc_info.type == NotImplementedError @@ -156,9 +155,7 @@ def test_frozen_candidates_err(pytestconfig, family, versions, ports, boards, co ), # find v1.18 NUCLEO_F091RC boards ], ) -def test_worklist( - family: str, versions: Union[List[str], str], ports: str, boards: str, count: int -): +def test_worklist(family: str, versions: Union[List[str], str], ports: str, boards: str, count: int): wl = build_worklist(family=family, versions=versions, ports=ports, boards=boards) assert isinstance(wl, list) msg = ", ".join(sorted([f"{l['version']}-{l['port']}-{l['board']}" for l in wl])) diff --git a/tests/publish/test_merge.py b/tests/publish/test_merge.py index eec6c1856..32a75708d 100644 --- a/tests/publish/test_merge.py +++ b/tests/publish/test_merge.py @@ -1,7 +1,9 @@ from pathlib import Path + import pytest from mock import MagicMock -from stubber.publish.merge_docstubs import merge_all_docstubs, copy_and_merge_docstubs + +from stubber.publish.merge_docstubs import copy_and_merge_docstubs, merge_all_docstubs from .fakeconfig import FakeConfig @@ -25,8 +27,12 @@ def test_merge_all_docstubs_mocked(mocker, tmp_path, pytestconfig): {"family": "micropython", "version": "1.19.1", "port": "esp32", "board": "generic"}, ], ) - m_copy_and_merge_docstubs: MagicMock = mocker.patch("stubber.publish.merge_docstubs.copy_and_merge_docstubs", autospec=True) - m_add_machine_pin_call: MagicMock = mocker.patch("stubber.publish.merge_docstubs.add_machine_pin_call", autospec=True) + m_copy_and_merge_docstubs: MagicMock = mocker.patch( + "stubber.publish.merge_docstubs.copy_and_merge_docstubs", autospec=True + ) + m_add_machine_pin_call: MagicMock = mocker.patch( + "stubber.publish.merge_docstubs.add_machine_pin_call", autospec=True + ) # mock pathlib.Path.exists to return True so there is no dependency of folders existing on the test system mocker.patch("stubber.publish.merge_docstubs.Path.exists", autospec=True, return_value=True) @@ -44,7 +50,9 @@ def test_copydocstubs_mocked(mocker, tmp_path, pytestconfig): config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.merge_docstubs.CONFIG", config) - m_enrich_folder: MagicMock = mocker.patch("stubber.publish.merge_docstubs.enrich_folder", autospec=True, return_value=42) + m_enrich_folder: MagicMock = mocker.patch( + "stubber.publish.merge_docstubs.enrich_folder", autospec=True, return_value=42 + ) m_copytree: MagicMock = mocker.patch("stubber.publish.merge_docstubs.shutil.copytree", autospec=True) m_copy: MagicMock = mocker.patch("stubber.publish.merge_docstubs.shutil.copy", autospec=True) @@ -57,4 +65,3 @@ def test_copydocstubs_mocked(mocker, tmp_path, pytestconfig): assert result == 42 assert m_enrich_folder.call_count == 1 assert m_copytree.call_count == 1 - assert m_copy.call_count == 1 diff --git a/tests/publish/test_multi_build.py b/tests/publish/test_multi_build.py index 0d44c5ea8..1c31ca83a 100644 --- a/tests/publish/test_multi_build.py +++ b/tests/publish/test_multi_build.py @@ -1,5 +1,7 @@ """Test publish module""" + from pathlib import Path + import pytest from mock import MagicMock from pytest_mock import MockerFixture @@ -23,15 +25,11 @@ def test_build_no_change(mocker: MockerFixture, tmp_path: Path, pytestconfig: py config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) # type: ignore + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) # type: ignore - m_check: MagicMock = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) # ----------------------------------------------------------------------------------------------- # Test build: not changed :--> should not build or publish @@ -54,23 +52,23 @@ def test_build_changed(mocker: MockerFixture, tmp_path: Path, pytestconfig: pyte config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False - ) - - m_check: MagicMock = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) + # mock the package on disk + mocker.patch("stubber.publish.package.StubPackage.are_package_sources_available", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_package_files", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_pyproject_stubs", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.calculate_hash", autospec=True, return_value="hash") + + m_is_changed.return_value = True # ----------------------------------------------------------------------------------------------- # Test publish - changed :--> should build and publish # ----------------------------------------------------------------------------------------------- - m_is_changed.return_value = True - result = build_multiple(production=False, ports=["stm32"], versions=["latest"]) + result = build_multiple(production=False, ports=["stm32"], versions=["preview"]) + # ----------------------------------------------------------------------------------------------- assert len(result) > 0 assert m_p_build.call_count >= 1 assert m_p_publish.call_count == 0 @@ -88,20 +86,16 @@ def test_build_force(mocker: MockerFixture, tmp_path: Path, pytestconfig: pytest config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False - ) - - m_check: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.check", autospec=True, return_value=True - ) - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) - + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) + + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) + # mock the package on disk + mocker.patch("stubber.publish.package.StubPackage.are_package_sources_available", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_package_files", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_pyproject_stubs", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.calculate_hash", autospec=True, return_value="hash") # ----------------------------------------------------------------------------------------------- # Test publish - not changed + Force :--> should build and publish m_p_build.reset_mock() @@ -110,7 +104,8 @@ def test_build_force(mocker: MockerFixture, tmp_path: Path, pytestconfig: pytest m_is_changed.reset_mock() # ----------------------------------------------------------------------------------------------- m_is_changed.return_value = False - result = build_multiple(production=False, force=True, ports=["stm32"], versions=["latest"]) + result = build_multiple(production=False, force=True, ports=["stm32"], versions=["preview"]) + # ----------------------------------------------------------------------------------------------- assert len(result) > 0 assert m_p_build.call_count >= 1 assert m_p_publish.call_count == 0 diff --git a/tests/publish/test_multi_publish.py b/tests/publish/test_multi_publish.py index 959d67b3b..d0250ec59 100644 --- a/tests/publish/test_multi_publish.py +++ b/tests/publish/test_multi_publish.py @@ -1,4 +1,5 @@ """Test publish module""" + from pathlib import Path import pytest @@ -22,15 +23,11 @@ def test_publish_no_change(mocker: MockerFixture, tmp_path: Path, pytestconfig: config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) # type: ignore + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) # type: ignore - m_check: MagicMock = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) # ----------------------------------------------------------------------------------------------- # Test publish: not changed :--> should not build or publish @@ -53,23 +50,23 @@ def test_publish_changed(mocker: MockerFixture, tmp_path: Path, pytestconfig: py config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False - ) - - m_check: MagicMock = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) + + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) + # mock the package on disk + mocker.patch("stubber.publish.package.StubPackage.are_package_sources_available", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_package_files", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_pyproject_stubs", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.calculate_hash", autospec=True, return_value="hash") # ----------------------------------------------------------------------------------------------- # Test publish - changed :--> should build and publish # ----------------------------------------------------------------------------------------------- m_is_changed.return_value = True - result = publish_multiple(production=False, ports=["stm32"], versions=["latest"]) + result = publish_multiple(production=False, ports=["stm32"], versions=["preview"]) + # ----------------------------------------------------------------------------------------------- assert len(result) > 0 assert m_p_build.call_count >= 1, "Build should be called" assert m_p_publish.call_count >= 1, "Publish should be called" @@ -87,17 +84,11 @@ def test_publish_build(mocker: MockerFixture, tmp_path: Path, pytestconfig: pyte config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False - ) - - m_check: MagicMock = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) + + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) # ----------------------------------------------------------------------------------------------- # Test publish - unchanged + explicit build :--> should not build and not publish @@ -121,25 +112,22 @@ def test_publish_build_force(mocker: MockerFixture, tmp_path: Path, pytestconfig config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False - ) - - m_check: MagicMock = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) + + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) # type: ignore + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) + # mock the package on disk + mocker.patch("stubber.publish.package.StubPackage.are_package_sources_available", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_package_files", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_pyproject_stubs", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.calculate_hash", autospec=True, return_value="hash") # ----------------------------------------------------------------------------------------------- # Test publish - unchanged + explicit build :--> should build but not publish # ----------------------------------------------------------------------------------------------- m_is_changed.return_value = False - result = publish_multiple( - production=False, build=True, force=True, ports=["stm32"], versions=["latest"] - ) + result = publish_multiple(production=False, build=True, force=True, ports=["stm32"], versions=["preview"]) assert len(result) > 0 assert m_p_build.call_count >= 1, "Build should be called" assert m_p_publish.call_count >= 1, "Publish should be called" @@ -157,20 +145,16 @@ def test_publish_force(mocker: MockerFixture, tmp_path: Path, pytestconfig: pyte config = FakeConfig(tmp_path=tmp_path, rootpath=pytestconfig.rootpath) mocker.patch("stubber.publish.publish.CONFIG", config) mocker.patch("stubber.publish.stubpackage.CONFIG", config) - m_is_changed: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False - ) - - m_check: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.check", autospec=True, return_value=True - ) - m_p_build: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True - ) - m_p_publish: MagicMock = mocker.patch( - "stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True - ) - + m_is_changed = mocker.patch("stubber.publish.package.StubPackage.is_changed", autospec=True, return_value=False) + + m_check = mocker.patch("stubber.publish.package.StubPackage.check", autospec=True, return_value=True) + m_p_build = mocker.patch("stubber.publish.package.StubPackage.poetry_build", autospec=True, return_value=True) + m_p_publish = mocker.patch("stubber.publish.package.StubPackage.poetry_publish", autospec=True, return_value=True) + # mock the package on disk + mocker.patch("stubber.publish.package.StubPackage.are_package_sources_available", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_package_files", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.update_pyproject_stubs", autospec=True, return_value=True) + mocker.patch("stubber.publish.package.StubPackage.calculate_hash", autospec=True, return_value="hash") # ----------------------------------------------------------------------------------------------- # Test publish - not changed + Force :--> should build and publish m_p_build.reset_mock() @@ -179,7 +163,7 @@ def test_publish_force(mocker: MockerFixture, tmp_path: Path, pytestconfig: pyte m_is_changed.reset_mock() # ----------------------------------------------------------------------------------------------- m_is_changed.return_value = False - result = publish_multiple(production=False, force=True, ports=["stm32"], versions=["latest"]) + result = publish_multiple(production=False, force=True, ports=["stm32"], versions=["preview"]) assert len(result) > 0 assert m_p_build.call_count >= 1, "Build should be called" assert m_p_publish.call_count >= 1, "Publish should be called" diff --git a/tests/publish/test_pathnames.py b/tests/publish/test_pathnames.py new file mode 100644 index 000000000..09df7fd1a --- /dev/null +++ b/tests/publish/test_pathnames.py @@ -0,0 +1,42 @@ +import pytest + +from stubber.publish.pathnames import board_folder_name, get_base + + +@pytest.mark.parametrize( + "expected_base, candidate, version", + [ + ("micropython-v1_22", {"family": "MicroPython", "version": "1.22"}, None), + ("micropython-v1_22_1", {"family": "MicroPython", "version": "1.22"}, "1.22.1"), + ("micropython-v1_22_2", {"family": "MicroPython"}, "1.22.2"), + ("micropython-v1_23_0_preview", {"family": "MicroPython", "version": "1.23.0-preview"}, None), + ], +) +def test_get_base_with_version(expected_base, candidate, version): + base = get_base(candidate, version) + assert base == expected_base + + +@pytest.mark.parametrize( + "expected_folder_name, fw, version ", + [ + ( + "micropython-v1_22_1-esp8266-ESP8266_GENERIC", + {"family": "MicroPython", "port": "esp8266", "board": "ESP8266_GENERIC", "version": "1.22.1"}, + None, + ), + ( + "micropython-v1_23_4-esp8266-ESP8266_GENERIC", + {"family": "MicroPython", "port": "esp8266", "board": "ESP8266_GENERIC", "version": "1.23.4"}, + "1.23.4", + ), + ( + "micropython-v1_23_5_preview-esp8266-ESP8266_GENERIC", + {"family": "MicroPython", "port": "esp8266", "board": "ESP8266_GENERIC", "version": "1.23.5-preview"}, + "1.23.5-preview", + ), + ], +) +def test_board_folder_name(expected_folder_name: str, fw: dict, version: str): + folder_name = board_folder_name(fw, version=version) + assert folder_name == expected_folder_name diff --git a/tests/util_test.py b/tests/util_test.py index 090f4e25c..609960cb4 100644 --- a/tests/util_test.py +++ b/tests/util_test.py @@ -12,50 +12,60 @@ @pytest.mark.parametrize( - "commit, build, clean", + "commit, build, expected", [ ("v1.13-103-gb137d064e", True, "v1.13-103"), + ("v1.13-103-gb137d064e", False, "preview"), # used to be 'latest' ("v1.13", True, "v1.13"), - ("v1.13-dirty", True, "v1.13"), - ("v1.13-103-gb137d064e", False, "latest"), # "v1.13-Latest"), ("v1.13", False, "v1.13"), - ("v1.13-dirty", False, "latest"), # "v1.13-Latest"), + ("v1.13-dirty", True, "v1.13"), + ("v1.13-dirty", False, "preview"), # used to be 'latest' + # lets keep all the preview tags simple based on the provided version + ("v1.23.0-preview", False, "v1.23.0-preview"), + ("v1.23.0-preview", True, "v1.23.0-preview"), + ("1.20.0-preview-487", False, "v1.20.0-preview"), + ("1.20.0-preview-487", True, "v1.20.0-preview"), + ("v1.23.0-preview-87-g0285cb2bf", False, "v1.23.0-preview"), + ], +) +def test_clean_version_build(commit, build, expected): + assert utils.clean_version(commit, build=build) == expected + + +@pytest.mark.parametrize( + "input, expected", + [ + ("", ""), + ("latest", "preview"), + ("preview", "preview"), + ("v1.23.0-preview-87-g0285cb2bf", "v1_23_0_preview"), ], ) -def test_clean_version_build(commit, build, clean): - assert utils.clean_version(commit, build=build) == clean +def test_clean_version_flat_preview(input: str, expected: str): + assert utils.clean_version(input, drop_v=False, flat=True) == expected def test_clean_version_special(): - assert utils.clean_version("v1.13.0-103-gb137d064e") == "latest" + assert utils.clean_version("v1.13.0-103-gb137d064e") == "preview" assert utils.clean_version("v1.13.0-103-gb137d064e", build=True) == "v1.13-103" - assert ( - utils.clean_version("v1.13.0-103-gb137d064e", build=True, commit=True) - == "v1.13-103-gb137d064e" - ) + assert utils.clean_version("v1.13.0-103-gb137d064e", build=True, commit=True) == "v1.13-103-gb137d064e" # with path # assert utils.clean_version("v1.13.0-103-gb137d064e", patch=True) == "v1.13.0-Latest" - assert utils.clean_version("v1.13.0-103-gb137d064e", patch=True) == "latest" + assert utils.clean_version("v1.13.0-103-gb137d064e", patch=True) == "preview" assert utils.clean_version("v1.13.0-103-gb137d064e", patch=True, build=True) == "v1.13.0-103" # with commit assert ( - utils.clean_version("v1.13.0-103-gb137d064e", patch=True, build=True, commit=True) - == "v1.13.0-103-gb137d064e" + utils.clean_version("v1.13.0-103-gb137d064e", patch=True, build=True, commit=True) == "v1.13.0-103-gb137d064e" ) # FLats # assert utils.clean_version("v1.13.0-103-gb137d064e", flat=True) == "v1_13-Latest" - assert utils.clean_version("v1.13.0-103-gb137d064e", flat=True) == "latest" - assert ( - utils.clean_version("v1.13.0-103-gb137d064e", build=True, commit=True, flat=True) - == "v1_13-103-gb137d064e" - ) + assert utils.clean_version("v1.13.0-103-gb137d064e", flat=True) == "preview" + assert utils.clean_version("v1.13.0-103-gb137d064e", build=True, commit=True, flat=True) == "v1_13_103_gb137d064e" # all options , no V for version assert ( - utils.clean_version( - "v1.13.0-103-gb137d064e", patch=True, build=True, commit=True, flat=True, drop_v=True - ) - == "1_13_0-103-gb137d064e" + utils.clean_version("v1.13.0-103-gb137d064e", patch=True, build=True, commit=True, flat=True, drop_v=True) + == "1_13_0_103_gb137d064e" ) @@ -109,14 +119,10 @@ def test_post_processing(tmp_path, pytestconfig, mocker: MockerFixture): dest = tmp_path / "stubs" # shutil.copytree(source, dest) - m_generate_pyi_files: MagicMock = mocker.patch( - "stubber.utils.post.generate_pyi_files", autospec=True - ) + m_generate_pyi_files = mocker.patch("stubber.utils.post.generate_pyi_files", autospec=True) return_val = SimpleNamespace() return_val.returncode = 0 - m_spr: MagicMock = mocker.patch( - "stubber.utils.post.subprocess.run", autospec=True, return_value=return_val - ) + m_spr = mocker.patch("stubber.utils.post.subprocess.run", autospec=True, return_value=return_val) utils.do_post_processing([dest], stubgen=True, black=True, autoflake=False) @@ -168,6 +174,4 @@ def test_make_stub_files_issues(tmp_path, pytestconfig): except ValueError: pass - assert ( - len(py_files) == PROBLEMATIC - ), "py and pyi files should match 1:1 and stored in the same folder" + assert len(py_files) == PROBLEMATIC, "py and pyi files should match 1:1 and stored in the same folder" diff --git a/tests/utils/repos_test.py b/tests/utils/repos_test.py index 6ddfa877f..e3b9e564d 100644 --- a/tests/utils/repos_test.py +++ b/tests/utils/repos_test.py @@ -12,4 +12,3 @@ def test_stubber_switch_version_commit_list(version: str): # from version 1.12.0, the commit list is not needed as micropython-lib is a submodule of micropython assert len(mpy_lib_commits) > 0 assert version in mpy_lib_commits, "match" - # TODO : check latest / master