From 3adfc9d198d980e88c858c45f7469ce1e1071f43 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Sat, 26 Sep 2026 07:55:08 -0500 Subject: [PATCH 1/2] multimer: python -c is a batch entry, not an interactive prompt On CPython, interactive() treated any __main__ without __file__ as a bare prompt, which includes python -c. So a timer armed at the top of a -c one-liner chose the ambient strategy, App.run() returned at once and the process exited, where -m and a script file keep the app running. Consult batch() as the MicroPython branch already does, and let _cmdline_tokens fall back to sys.orig_argv where there is no /proc. --- lib/multimer/_hostloop.py | 8 +++++++- tests/test_hostloop.py | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/multimer/_hostloop.py b/lib/multimer/_hostloop.py index 3db7205b..dec03ba1 100644 --- a/lib/multimer/_hostloop.py +++ b/lib/multimer/_hostloop.py @@ -94,6 +94,10 @@ def _cmdline_tokens(): return _win32_cmdline() except Exception: pass + # CPython 3.10+ keeps the flags itself (macOS has no /proc). + argv = getattr(sys, "orig_argv", None) + if argv: + return tuple(argv) return () @@ -183,7 +187,9 @@ def interactive(): flags = getattr(sys, "flags", None) if getattr(flags, "interactive", 0): return True - return _main_file() is None + # ``python -c`` has no ``__main__.__file__`` either, but no prompt + # follows it: it is a batch entry, like ``-m``. + return _main_file() is None and not batch() if _mcu() and _impl() == "circuitpython": # CircuitPython cannot tell code.py from the REPL, and delivers # nothing in the background either way: the exit hook is the only diff --git a/tests/test_hostloop.py b/tests/test_hostloop.py index fb589b2d..3d6e7785 100644 --- a/tests/test_hostloop.py +++ b/tests/test_hostloop.py @@ -121,6 +121,19 @@ def test_circuitpython_firmware_is_never_interactive(self): self._fake_impl("circuitpython", "rp2") self.assertFalse(_hostloop.interactive()) + def test_cpython_dash_c_is_not_interactive(self): + """``python -c`` has no ``__main__.__file__``, and no prompt follows it.""" + self._patch(_hostloop, "_impl", lambda: "cpython") + self._patch(_hostloop, "_main_file", lambda: None) + self._patch(_hostloop, "_cmdline_tokens", lambda: ("python", "-c", "import app")) + self.assertFalse(_hostloop.interactive()) + + def test_cpython_bare_prompt_is_interactive(self): + self._patch(_hostloop, "_impl", lambda: "cpython") + self._patch(_hostloop, "_main_file", lambda: None) + self._patch(_hostloop, "_cmdline_tokens", lambda: ("python",)) + self.assertTrue(_hostloop.interactive()) + def test_circuitpython_firmware_takes_the_exit_hook(self): self._fake_impl("circuitpython", "rp2") self._patch(_hostloop, "_cmdline_tokens", lambda: ()) From e551777583ff150be59c061e238b5f88c705ba88 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Sat, 26 Sep 2026 08:18:49 -0500 Subject: [PATCH 2/2] timing ledger: the browser follow-ups pass; the phone visual pass is still owed --- docs/timing-design.md | 21 +++++++++++++++------ docs/timing-hardware-tests.md | 8 ++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/timing-design.md b/docs/timing-design.md index 28837028..e4a64a4f 100644 --- a/docs/timing-design.md +++ b/docs/timing-design.md @@ -539,12 +539,21 @@ runs, not the cloud's; the cloud's container was 4 cores, the bench is 8. delivery, as designed: 84 callbacks a second through `sleep_ms` with the main thread idle, 0 while it spins, and `report()` answers over the serial console. Same mechanism as the unix build in Phase 4. - - **Pending:** the LVGL launcher and drum machine on the phone (kept the - screen at brightness 1 for photosensitivity and stayed within the P4/phone - windows); the mechanism they would exercise, main-thread bytecode - delivery, is what the Android numbers already prove. PyScript/Pyodide - pages (the `asyncio` source, the same code the Jupyter proof runs) and an - `mp-wasm` rebuild carrying the bridge fix remain the two browser follow-ups. + - **Follow-ups (2026-09-26, after 0.6.1).** The browser checks are done. + Under PyScript/Pyodide, `lv_test_timer` shows `Timer: asyncio/idle` and + its seconds count and arc keep moving. An `mp-wasm` rebuild carrying the + bridge fix passes `wasm_host.mjs` under node, runs `lv_test_timer` in the + direct gallery host (`Timer: wasm/idle`), brings all 20 portal heroes + up with no errors, and passes a new workbench timers test. The runtime + that shipped with 0.6.1 threw `function signature mismatch` on every + timer callback in all three places. The vendored runtimes are in + PyDevices.github.io#9 and workbench#1, and the runner's + `MULTIMER_BACKEND` line is dropped with its recipes moved to 0.6.1 in + android-runner#24. + **Still pending:** the LVGL launcher and drum machine visual pass on the + phone. An APK from android-runner#24 builds and installs, but the S21's + secure lock screen kept the activity from ever drawing, so nothing was + seen. The desktop launcher on 0.6.1 was checked under Xvfb instead. - **Phase 6, the deliverables (here).** The four repository series were exported with `git format-patch` from branches on each repository's `origin/main`, then re-applied with `git am` onto a fresh checkout of each diff --git a/docs/timing-hardware-tests.md b/docs/timing-hardware-tests.md index f8613e35..13102d20 100644 --- a/docs/timing-hardware-tests.md +++ b/docs/timing-hardware-tests.md @@ -218,5 +218,9 @@ per-plan record of pass/fail and anything found. mechanism it exercises is what the numbers already prove. `--install-apk` installs the 0.2.3 release; a prior local-key debug build must be uninstalled first. -- **Not run:** PyScript/Pyodide in a browser (the `asyncio` source, proven via - Jupyter in the cloud) and an `mp-wasm` rebuild carrying the bridge fix. +- **Browsers, 2026-09-26 — pass.** PyScript/Pyodide: `lv_test_timer` reports + `Timer: asyncio/idle` and animates. The `mp-wasm` rebuild with the bridge fix + runs the same demo in the direct gallery host (`Timer: wasm/idle`, 298 → 495 + deliveries in a second), all 20 portal heroes, and workbench's simulator. The + runtime that shipped with 0.6.1 threw `function signature mismatch` on every + timer callback. Details: [timing-design.md](timing-design.md#ledger).