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