From 13318daf5bd34e4aeab147872cccb3ebe4b3fc27 Mon Sep 17 00:00:00 2001 From: Me Here Date: Fri, 29 May 2026 14:17:12 -0500 Subject: [PATCH] PM_K-1 0.0.11: fix boot crash - CircuitPython str has no .isalnum() With the .mpy loading (no more OOM), the full app ran on hardware for the first time and hit _slkey()'s c.isalnum() in the set-list dedup -> AttributeError (MicroPython/CircuitPython str omits isalnum; my CPython harness has it, so it slipped through). Replaced with a membership test against an explicit alnum set (uses only .lower()). Also compile the .mpy from inside pico-cp/ so tracebacks read "app.py" instead of "pico-cp/app.py". Co-Authored-By: Claude Opus 4.7 (1M context) --- build.sh | 4 ++-- pico-cp/__pycache__/app.cpython-312.pyc | Bin 79459 -> 79494 bytes pico-cp/app.py | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.sh b/build.sh index bd01dc9..a0633a7 100755 --- a/build.sh +++ b/build.sh @@ -15,9 +15,9 @@ mkdir -p dist # Precompile the PM_K-1 CircuitPython firmware to .mpy. CircuitPython compiles a ~56KB .py at boot, # which fragments the heap and OOMs on the RP2040; a precompiled .mpy loads without compiling. Needs # Adafruit's mpy-cross matching the device's CircuitPython (10.2.1) -> emits CircuitPython mpy v6. -MPYC="tools/mpy-cross" +MPYC="$PWD/tools/mpy-cross"; ROOT="$PWD" [[ -x "$MPYC" ]] || { echo "error: $MPYC missing (Adafruit mpy-cross for CircuitPython 10.2.1)" >&2; exit 1; } -"$MPYC" pico-cp/app.py -o dist/app.mpy +( cd pico-cp && "$MPYC" app.py -o "$ROOT/dist/app.mpy" ) # compile from pico-cp/ so tracebacks read "app.py" echo "precompiled dist/app.mpy ($(stat -c%s dist/app.mpy) bytes <- $(stat -c%s pico-cp/app.py) source)" python3 - <<'PY' diff --git a/pico-cp/__pycache__/app.cpython-312.pyc b/pico-cp/__pycache__/app.cpython-312.pyc index 511ca0588d521ed1e0d7a143b7c993185456807d..0d4042bf2c0f7212a3d3d05432e1d4258d6d4cc8 100644 GIT binary patch delta 7296 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 delta 7255 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 IBniU*2e-!f^Z)<= diff --git a/pico-cp/app.py b/pico-cp/app.py index d0e97ac..dbc1287 100644 --- a/pico-cp/app.py +++ b/pico-cp/app.py @@ -18,7 +18,7 @@ import board, busio, digitalio, analogio, pwmio, displayio, vectorio, time, json, gc, os, supervisor supervisor.runtime.autoreload = False # we write our own files (log + pushed programs); never self-restart -APP_VERSION = "0.0.10" # firmware version (the A/B updater pushes/compares this) +APP_VERSION = "0.0.11" # firmware version (the A/B updater pushes/compares this) try: import rtc # set from the editor's clock SysEx so the log has real timestamps except ImportError: @@ -292,8 +292,9 @@ def lane_to_str(L): # serialize a lane back if L['mute']: s += '!' return s -def _slkey(t): # normalise a title for built-in/user de-duplication - return "".join(c.lower() for c in t if c.isalnum()) +_ALNUM = "abcdefghijklmnopqrstuvwxyz0123456789" +def _slkey(t): # normalise a title for built-in/user de-dup (no str.isalnum on CircuitPython) + return "".join(c for c in t.lower() if c in _ALNUM) def load_user_setlists(): # User playlists from /programs.json (pushed by the editor). New {setlists:[{title,programs:[..]}]} form, # or the old flat {programs:[..]} (one list). Built-ins are baked in BUILTIN_SETLISTS, never here.