From 6af042d89e2f89488bd5c5a11e9e9f45e51d4f36 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sun, 20 Apr 2025 16:11:19 -0500 Subject: [PATCH 1/4] Add typechecking --- .github/workflows/ci.yaml | 7 +++ Makefile | 3 + main.py | 9 ++- pyproject.toml | 14 +++++ repl.py | 4 +- typings/board.pyi | 71 +++++++++++++++++++++++ typings/gc.pyi | 116 ++++++++++++++++++++++++++++++++++++++ uv.lock | 36 ++++++++++++ 8 files changed, 252 insertions(+), 8 deletions(-) create mode 100644 typings/board.pyi create mode 100644 typings/gc.pyi diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 77145f7..4a5d01f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,6 +14,13 @@ jobs: - name: Lint run: | make fmt + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Typecheck + run: | + make typecheck archive: runs-on: ubuntu-latest steps: diff --git a/Makefile b/Makefile index 66dab9a..d3ce0c5 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,9 @@ sync-time: uv ## Syncs th time from your computer to the PROVES Kit board fmt: pre-commit-install ## Lint and format files $(UVX) pre-commit run --all-files +typecheck: .venv ## Run type check + @$(UV) run -m pyright . + BOARD_MOUNT_POINT ?= "" VERSION ?= $(shell git tag --points-at HEAD --sort=-creatordate < /dev/null | head -n 1) diff --git a/main.py b/main.py index c8387c8..c80b7d9 100644 --- a/main.py +++ b/main.py @@ -13,6 +13,7 @@ import digitalio import microcontroller +from busio import SPI try: from board_definitions import proveskit_rp2040_v4 as board @@ -42,7 +43,7 @@ rtc = MicrocontrollerManager() logger: Logger = Logger( - error_counter=Counter(index=register.ERRORCNT, datastore=microcontroller.nvm), + error_counter=Counter(index=register.ERRORCNT), colorized=False, ) @@ -66,7 +67,7 @@ config: Config = Config("config.json") # TODO(nateinaction): fix spi init - spi0 = _spi_init( + spi0: SPI = _spi_init( logger, board.SPI0_SCK, board.SPI0_MOSI, @@ -76,7 +77,7 @@ radio = RFM9xManager( logger, config.radio, - Flag(index=register.FLAG, bit_index=7, datastore=microcontroller.nvm), + Flag(index=register.FLAG, bit_index=7), spi0, initialize_pin(logger, board.SPI0_CS0, digitalio.Direction.OUTPUT, True), initialize_pin(logger, board.RF1_RST, digitalio.Direction.OUTPUT, True), @@ -153,9 +154,7 @@ def main(): f.listen_loiter() - f.all_face_data() watchdog.pet() - f.send_face() f.listen_loiter() diff --git a/pyproject.toml b/pyproject.toml index 0402196..b7c3f27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "circuitpython-stubs==9.2.5", "coverage==7.6.10", "pre-commit==4.0.1", + "pyright[nodejs]==1.1.399", "pytest==8.3.2", ] @@ -49,3 +50,16 @@ directory = ".coverage-reports/html" [tool.coverage.xml] output = ".coverage-reports/coverage.xml" + +[tool.pyright] +include = ["main.py", "boot.py", "repl.py", "safemode.py"] +exclude = [ + "**/__pycache__", + ".venv", + ".git", + "artifacts", + "lib", + "typings", +] +stubPath = "./typings" +reportMissingModuleSource = false diff --git a/repl.py b/repl.py index 8e2f2a9..1ba2b67 100644 --- a/repl.py +++ b/repl.py @@ -1,5 +1,3 @@ -import microcontroller - import lib.pysquared.nvm.register as register from lib.pysquared.config.config import Config from lib.pysquared.logger import Logger @@ -7,7 +5,7 @@ from lib.pysquared.satellite import Satellite logger: Logger = Logger( - error_counter=Counter(index=register.ERRORCNT, datastore=microcontroller.nvm), + error_counter=Counter(index=register.ERRORCNT), colorized=False, ) config: Config = Config("config.json") diff --git a/typings/board.pyi b/typings/board.pyi new file mode 100644 index 0000000..9d47248 --- /dev/null +++ b/typings/board.pyi @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers +# +# SPDX-License-Identifier: MIT +""" +Board stub for PROVES Kit v4 + - port: raspberrypi + - board_id: proveskit_rp2040_v4 + - NVM size: 4096 + - Included modules: _asyncio, _bleio, _pixelmap, adafruit_bus_device, adafruit_pixelbuf, aesio, alarm, analogbufio, analogio, array, atexit, audiobusio, audiocore, audiomixer, audiomp3, audiopwmio, binascii, bitbangio, bitmapfilter, bitmaptools, bitops, board, builtins, builtins.pow3, busdisplay, busio, busio.SPI, busio.UART, codeop, collections, countio, digitalio, displayio, epaperdisplay, errno, floppyio, fontio, fourwire, framebufferio, getpass, gifio, hashlib, i2cdisplaybus, i2ctarget, imagecapture, io, jpegio, json, keypad, keypad.KeyMatrix, keypad.Keys, keypad.ShiftRegisterKeys, keypad_demux, keypad_demux.DemuxKeyMatrix, locale, math, memorymap, microcontroller, msgpack, neopixel_write, nvm, onewireio, os, os.getenv, paralleldisplaybus, pulseio, pwmio, qrio, rainbowio, random, re, rgbmatrix, rotaryio, rp2pio, rtc, sdcardio, select, sharpdisplay, storage, struct, supervisor, synthio, sys, terminalio, tilepalettemapper, time, touchio, traceback, ulab, usb, usb_cdc, usb_hid, usb_host, usb_midi, usb_video, vectorio, warnings, watchdog, zlib + - Frozen libraries: +--- +proveskit: Borrowed from circuitpython-stubs https://pypi.org/project/circuitpython-stubs/#files board definitions +""" + +# Imports +import busio +import microcontroller + +# Board Info: +board_id: str + +# Pins: +SPI0_CS1: microcontroller.Pin # GPIO26 +NEO_PWR: microcontroller.Pin # GPIO27 +SPI0_CS2: microcontroller.Pin # GPIO28 +D0: microcontroller.Pin # GPIO29 +D8: microcontroller.Pin # GPIO18 +D9: microcontroller.Pin # GPIO19 +D6: microcontroller.Pin # GPIO16 +TX: microcontroller.Pin # GPIO0 +RX: microcontroller.Pin # GPIO1 +I2C1_SDA: microcontroller.Pin # GPIO2 +I2C1_SCL: microcontroller.Pin # GPIO3 +I2C0_SDA: microcontroller.Pin # GPIO4 +I2C0_SCL: microcontroller.Pin # GPIO5 +PC: microcontroller.Pin # GPIO6 +VS: microcontroller.Pin # GPIO7 +SPI0_MISO: microcontroller.Pin # GPIO8 +SPI0_CS0: microcontroller.Pin # GPIO9 +SPI0_SCK: microcontroller.Pin # GPIO10 +SPI0_MOSI: microcontroller.Pin # GPIO11 +D2: microcontroller.Pin # GPIO12 +D3: microcontroller.Pin # GPIO13 +D4: microcontroller.Pin # GPIO14 +D5: microcontroller.Pin # GPIO15 +RF1_RST: microcontroller.Pin # GPIO20 +WDT_WDI: microcontroller.Pin # GPIO21 +RF1_IO4: microcontroller.Pin # GPIO22 +RF1_IO0: microcontroller.Pin # GPIO23 +NEOPIX: microcontroller.Pin # GPIO24 +HS: microcontroller.Pin # GPIO25 +D7: microcontroller.Pin # GPIO17 + +# Members: +def I2C() -> busio.I2C: + """Returns the `busio.I2C` object for the board's designated I2C bus(es). + The object created is a singleton, and uses the default parameter values for `busio.I2C`. + """ + +def SPI() -> busio.SPI: + """Returns the `busio.SPI` object for the board's designated SPI bus(es). + The object created is a singleton, and uses the default parameter values for `busio.SPI`. + """ + +def UART() -> busio.UART: + """Returns the `busio.UART` object for the board's designated UART bus(es). + The object created is a singleton, and uses the default parameter values for `busio.UART`. + """ + +# Unmapped: +# none diff --git a/typings/gc.pyi b/typings/gc.pyi new file mode 100644 index 0000000..56dfe3b --- /dev/null +++ b/typings/gc.pyi @@ -0,0 +1,116 @@ +""" +Control the garbage collector. + +MicroPython module: https://docs.micropython.org/en/v1.25.0/library/gc.html + +CPython module: :mod:`python:gc` https://docs.python.org/3/library/gc.html . + +--- +Module: 'gc' on micropython-v1.25.0-rp2-RPI_PICO +--- +proveskit: Borrowed from https://github.com/Josverl/micropython-stubs +https://pypi.org/project/micropython-rp2-stubs/#files +""" + +# MCU: {'build': '', 'ver': '1.25.0', 'version': '1.25.0', 'port': 'rp2', 'board': 'RPI_PICO', 'mpy': 'v6.3', 'family': 'micropython', 'cpu': 'RP2040', 'arch': 'armv6m'} +# Stubber: v1.24.0 +from __future__ import annotations + +from typing import overload + +from _typeshed import Incomplete + +def mem_alloc() -> int: + """ + Return the number of bytes of heap RAM that are allocated by Python code. + + Admonition:Difference to CPython + :class: attention + + This function is MicroPython extension. + """ + ... + +def isenabled(*args, **kwargs) -> Incomplete: ... +def mem_free() -> int: + """ + Return the number of bytes of heap RAM that is available for Python + code to allocate, or -1 if this amount is not known. + + Admonition:Difference to CPython + :class: attention + + This function is MicroPython extension. + """ + ... + +@overload +def threshold() -> int: + """ + Set or query the additional GC allocation threshold. Normally, a collection + is triggered only when a new allocation cannot be satisfied, i.e. on an + out-of-memory (OOM) condition. If this function is called, in addition to + OOM, a collection will be triggered each time after *amount* bytes have been + allocated (in total, since the previous time such an amount of bytes + have been allocated). *amount* is usually specified as less than the + full heap size, with the intention to trigger a collection earlier than when the + heap becomes exhausted, and in the hope that an early collection will prevent + excessive memory fragmentation. This is a heuristic measure, the effect + of which will vary from application to application, as well as + the optimal value of the *amount* parameter. + + Calling the function without argument will return the current value of + the threshold. A value of -1 means a disabled allocation threshold. + + Admonition:Difference to CPython + :class: attention + + This function is a MicroPython extension. CPython has a similar + function - ``set_threshold()``, but due to different GC + implementations, its signature and semantics are different. + """ + +@overload +def threshold(amount: int) -> None: + """ + Set or query the additional GC allocation threshold. Normally, a collection + is triggered only when a new allocation cannot be satisfied, i.e. on an + out-of-memory (OOM) condition. If this function is called, in addition to + OOM, a collection will be triggered each time after *amount* bytes have been + allocated (in total, since the previous time such an amount of bytes + have been allocated). *amount* is usually specified as less than the + full heap size, with the intention to trigger a collection earlier than when the + heap becomes exhausted, and in the hope that an early collection will prevent + excessive memory fragmentation. This is a heuristic measure, the effect + of which will vary from application to application, as well as + the optimal value of the *amount* parameter. + + Calling the function without argument will return the current value of + the threshold. A value of -1 means a disabled allocation threshold. + + Admonition:Difference to CPython + :class: attention + + This function is a MicroPython extension. CPython has a similar + function - ``set_threshold()``, but due to different GC + implementations, its signature and semantics are different. + """ + +def collect() -> None: + """ + Run a garbage collection. + """ + ... + +def enable() -> None: + """ + Enable automatic garbage collection. + """ + ... + +def disable() -> None: + """ + Disable automatic garbage collection. Heap memory can still be allocated, + and garbage collection can still be initiated manually using :meth:`gc.collect`. + """ + ... diff --git a/uv.lock b/uv.lock index c464954..1a0d04c 100644 --- a/uv.lock +++ b/uv.lock @@ -119,6 +119,7 @@ dependencies = [ { name = "circuitpython-stubs" }, { name = "coverage" }, { name = "pre-commit" }, + { name = "pyright", extra = ["nodejs"] }, { name = "pytest" }, ] @@ -128,6 +129,7 @@ requires-dist = [ { name = "circuitpython-stubs", specifier = "==9.2.5" }, { name = "coverage", specifier = "==7.6.10" }, { name = "pre-commit", specifier = "==4.0.1" }, + { name = "pyright", extras = ["nodejs"], specifier = "==1.1.399" }, { name = "pytest", specifier = "==8.3.2" }, ] @@ -222,6 +224,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] +[[package]] +name = "nodejs-wheel-binaries" +version = "22.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/c7/4fd3871d2b7fd5122216245e273201ab98eda92bbd6fe9ad04846b758c56/nodejs_wheel_binaries-22.14.0.tar.gz", hash = "sha256:c1dc43713598c7310d53795c764beead861b8c5021fe4b1366cb912ce1a4c8bf", size = 8055 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/b6/66ef4ef75ea7389ea788f2d5505bf9a8e5c3806d56c7a90cf46a6942f1cf/nodejs_wheel_binaries-22.14.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:d8ab8690516a3e98458041286e3f0d6458de176d15c14f205c3ea2972131420d", size = 50326597 }, + { url = "https://files.pythonhosted.org/packages/7d/78/023d91a293ba73572a643bc89d11620d189f35f205a309dd8296aa45e69a/nodejs_wheel_binaries-22.14.0-py2.py3-none-macosx_11_0_x86_64.whl", hash = "sha256:b2f200f23b3610bdbee01cf136279e005ffdf8ee74557aa46c0940a7867956f6", size = 51158258 }, + { url = "https://files.pythonhosted.org/packages/af/86/324f6342c79e5034a13319b02ba9ed1f4ac8813af567d223c9a9e56cd338/nodejs_wheel_binaries-22.14.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0877832abd7a9c75c8c5caafa37f986c9341ee025043c2771213d70c4c1defa", size = 57180264 }, + { url = "https://files.pythonhosted.org/packages/6d/9f/42bdaab26137e31732bff00147b9aca2185d475b5752b57a443e6c7ba93f/nodejs_wheel_binaries-22.14.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fded5a70a8a55c2135e67bd580d8b7f2e94fcbafcc679b6a2d5b92f88373d69", size = 57693251 }, + { url = "https://files.pythonhosted.org/packages/ab/d7/94f8f269aa86cf35f9ed2b70d09aca48dc971fb5656fdc4a3b69364b189f/nodejs_wheel_binaries-22.14.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c1ade6f3ece458b40c02e89c91d5103792a9f18aaad5026da533eb0dcb87090e", size = 58841717 }, + { url = "https://files.pythonhosted.org/packages/2d/a0/43b7316eaf22b4ee9bfb897ee36c724efceac7b89d7d1bedca28057b7be1/nodejs_wheel_binaries-22.14.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:34fa5ed4cf3f65cbfbe9b45c407ffc2fc7d97a06cd8993e6162191ff81f29f48", size = 59808791 }, + { url = "https://files.pythonhosted.org/packages/10/0a/814491f751a25136e37de68a2728c9a9e3c1d20494aba5ff3c230d5f9c2d/nodejs_wheel_binaries-22.14.0-py2.py3-none-win_amd64.whl", hash = "sha256:ca7023276327455988b81390fa6bbfa5191c1da7fc45bc57c7abc281ba9967e9", size = 40478921 }, + { url = "https://files.pythonhosted.org/packages/f4/5c/cab444afaa387dceac8debb817b52fd00596efcd2d54506c27311c6fe6a8/nodejs_wheel_binaries-22.14.0-py2.py3-none-win_arm64.whl", hash = "sha256:fd59c8e9a202221e316febe1624a1ae3b42775b7fb27737bf12ec79565983eaf", size = 36206637 }, +] + [[package]] name = "packaging" version = "24.2" @@ -277,6 +295,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/96/a8de7b7e5556d4b00d1ca1969fc34c89a1b6d177876c7a31d42631b090fc/pyftdi-0.56.0-py3-none-any.whl", hash = "sha256:3ef0baadbf9031dde9d623ae66fac2d16ded36ce1b66c17765ca1944cb38b8b0", size = 145718 }, ] +[[package]] +name = "pyright" +version = "1.1.399" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/9d/d91d5f6d26b2db95476fefc772e2b9a16d54c6bd0ea6bb5c1b6d635ab8b4/pyright-1.1.399.tar.gz", hash = "sha256:439035d707a36c3d1b443aec980bc37053fbda88158eded24b8eedcf1c7b7a1b", size = 3856954 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/b5/380380c9e7a534cb1783c70c3e8ac6d1193c599650a55838d0557586796e/pyright-1.1.399-py3-none-any.whl", hash = "sha256:55f9a875ddf23c9698f24208c764465ffdfd38be6265f7faf9a176e1dc549f3b", size = 5592584 }, +] + +[package.optional-dependencies] +nodejs = [ + { name = "nodejs-wheel-binaries" }, +] + [[package]] name = "pyserial" version = "3.5" From 7fbdae4c5bf72777c5b6eea6ad3e51ad4877d34c Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sun, 20 Apr 2025 16:16:17 -0500 Subject: [PATCH 2/4] Fix typecheck in CI --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d3ce0c5..78a411c 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ sync-time: uv ## Syncs th time from your computer to the PROVES Kit board fmt: pre-commit-install ## Lint and format files $(UVX) pre-commit run --all-files -typecheck: .venv ## Run type check +typecheck: .venv download-libraries ## Run type check @$(UV) run -m pyright . BOARD_MOUNT_POINT ?= "" From 70a47d9fc9a16022d6e7bf0408986a8a020877d0 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sun, 20 Apr 2025 16:31:04 -0500 Subject: [PATCH 3/4] Update pysquared version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 78a411c..ba8e5d3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYSQUARED_VERSION ?= v2.0.0-alpha-25w14-3 +PYSQUARED_VERSION ?= v2.0.0-alpha-25w17 PYSQUARED ?= git+https://github.com/proveskit/pysquared@$(PYSQUARED_VERSION) .PHONY: all From 5413d6760a9f6a87d11da15069211bdb0c81766f Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Tue, 22 Apr 2025 17:22:51 -0500 Subject: [PATCH 4/4] empty