Developer workflow only — local servers, test harnesses, and IDE typings. For repo maintenance see scripts/README.md.
| Script | Purpose |
|---|---|
serve.py |
HTTP server with Cross-Origin-Isolation headers |
pyscript.py |
Universal PyScript runner (on PATH from pydevices/bin) — pyscript.py -m bouncing_balls |
jupyter.py |
Standalone Jupyter runner (on PATH from pydevices/bin) — jupyter.py paint.py |
From repo root:
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt # playwright, pytest (optional)
.venv/bin/playwright install chromium # headless PyScript matrix
pyscript.py -m bouncing_balls
jupyter.py lib/examples/paint.pySee pydevices/docs/pyscript.md and pydevices/docs/jupyter.md.
| Script | Purpose |
|---|---|
lvgl_input_probe.py |
LVGL keypad-mapping diagnostic + selftests |
micropython tools/lvgl_input_probe.py --selftest
cd lib && micropython ../tools/lvgl_input_probe.py # interactive; focus the windowThe core displaydev/appdev probe is owned by
pydevices/tools/input_probe.py.
Diagnostic probes (ps_debug.py and ps_shot.py) live in pydevices/tools/:
python ../pydevices/tools/ps_debug.py http://127.0.0.1:8000/pydevices-examples/pyscript/harness.html?modules=bouncing_balls
python ../pydevices/tools/ps_shot.py http://127.0.0.1:8000/pydevices-examples/pyscript/harness.html?modules=bouncing_balls 3Source of truth for the cross-interpreter example test system: this section
(workflow), example_interpreters.toml (interpreter command
templates), and example_test_manifest.toml
(per-example metadata). Platform is the product category (see
pydevices/docs/displaydev.md); interpreter is the
concrete launcher used in automation.
| Script | Purpose |
|---|---|
example_test_kit.py |
Cross-interpreter example matrix |
example_test_manifest.toml |
Per-example metadata |
example_interpreters.toml |
Interpreter command templates |
sibling_repos.py |
Discover sibling lib/ paths for matrix runs |
.venv/bin/python -m unittest discover -s testsFor thorough verification (timer/multimer/interpreter changes, or "run the full
matrix"), prefer example-by-example, all selected interpreters in parallel
per example (--jobs 0, default) and --fail-fast. There is one timer
mode: multimer delivers on every host by itself, so the old sync/async split
(PYDEVICES_TIMER_ASYNC) is gone; the kit still accepts the variable and
ignores it.
| Interpreters |
|---|
7 — desktop SDL: micropython, micropython.exe, circuitpython, cpython-venv, python.exe; plus pyscript, jupyter |
| Android (opt-in) |
Default timing is already short (duration_s=2, timeout_s=15 in the
interpreters/manifest defaults). After each example's parallel wave finishes, if
any cell failed, stop before the next example; fix the root cause, then resume.
# PyScript needs the static server
python tools/serve.py # separate terminal; reuse if already on :8000
export SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy PYTHONUNBUFFERED=1
mkdir -p /tmp/pydevices-examples-matrix
RT="micropython micropython.exe circuitpython cpython-venv python.exe pyscript jupyter"
set -o pipefail # keep kit exit status through tee
stdbuf -oL -eL \
.venv/bin/python tools/example_test_kit.py --no-unit-tests --fail-fast \
--only-interpreter $RT \
--results-json /tmp/pydevices-examples-matrix/all.json \
2>&1 | stdbuf -oL -eL tee /tmp/pydevices-examples-matrix/all.logUnix subprocesses see that shell export. Windows .exe behavior is different —
see Windows PE under WSL.
micropython.exe and python.exe are Windows PE binaries launched from WSL.
They cannot read Linux-exported environment variables. The kit therefore
forwards only values that must cross that boundary via wrapper argv +
boarddev.env_set (notably --timer-async / --multimer-source).
Do not forward SDL_VIDEODRIVER / SDL_AUDIODRIVER to PE. Unix cells stay
headless from the shell SDL_*=dummy export; PE keeps a real Windows video
driver. During a matrix run you should see micropython.exe / python.exe
windows — that means the cell started and is usable. Forwarding dummy into
PE hides those windows.
summary: hang on PE is usually a quit failure, not a dead process. If the
Windows window stays up past duration_s / until the kit timeout_s, the
example is still running (you can interact with it); the harness timed out
waiting for cooperative quit / EXAMPLE_RESULT. PE child output is captured
via temp files so a timeout kill does not wipe stdout the way pipes often did.
Fix the quit path (wrapper deadline / pydevices_test_mode / inject) rather
than treating PE as “failed to launch.”
From native Windows. The kit also runs from PowerShell with python.org's
Python (3.11 or newer, for tomllib): it finds micropython.exe on PATH,
and nothing in its launch path needs WSL. With an exe built by
micropython-pydevices (--port windows --variant pydevices --modules all):
$env:PATH = "<micropython-pydevices>\builds\windows\pydevices;$env:PATH"
python tools/example_test_kit.py --no-unit-tests --only-interpreter micropython.exeThe exe has no threads, so the wrapper quits LVGL examples with a multimer
one-shot timer (pydevices_test_mode.arm_quit_timer()); the deadline hook
alone never fires there.
Scheduling: with --order examples and --jobs 0 (default), all
selected interpreters for an example — including both .exe launchers — run
concurrently.
Results: live Running <example> @ <interpreter>... lines on stderr; summary
table at end (or when --fail-fast stops). Full JSON defaults to the system
temp dir (example_test_results.json), not a path under the repo. Override
with --results-json PATH.
Real X display: DISPLAY=:1 (xfce) without dummy SDL opens a window titled
"<impl> on <platform>". Optional: xvfb-run -a … (no SDL_VIDEODRIVER=dummy)
for a real X11/SDL path without :1. Do not require Xvfb in the tools scripts;
wrap when useful. PyScript/Playwright does not need Xvfb.
Desktop matrices use repo .venv (cpython-venv) plus interpreters on
PATH / ~/bin (micropython, circuitpython). micropython.exe / python.exe are
Windows binaries and cannot run in a Linux cloud sandbox.
After changes to the C modules these binaries or the PyScript runtime carry, rebuild them with
micropython-pydevices' build_mp.py: for example
./build_mp.py --port unix --variant pydevices --modules all, and --port webassembly for the PyScript runtime.
micropython.exe matrix: no threading / _thread. The example wrapper
uses a appdev.App.poll deadline quit (not a multimer SDL quit timer). With
pydevices_test_mode.ENABLED, appdev.App skips auto-refresh wiring so examples
that call show() themselves avoid a competing SDL refresh timer. WSL PE
scheduling and SDL env rules:
Windows PE under WSL.
Examples that import palettes / pdwidgets / pygraphics / the ctypes
usdl2 fallback need those sibling lib/ dirs on path. The PyPI project
literally named palettes is unrelated — do not pip install palettes.
Prefer TestPyPI native builds for pygraphics and usdl2 when available.
Quick setup: bash scripts/setup_sibling_repos.sh (clones current main,
writes .pth files). The harness auto-discovers the same paths via
sibling_repos.py. pdwidgets also needs pydevices's lib on path
(the harness adds it).
tools/png_test.pyin pdwidgets (PNG probe) needsPDWIDGETS_PNG_DIR/ material-design-icons and a sibling pydevices-examples checkout.
Start or reuse python tools/serve.py, then re-run with --only-interpreter pyscript.
Headless needs Playwright (.venv/bin/pip install -r requirements-dev.txt and
.venv/bin/playwright install chromium). Without it, pyscript cells report
needs_playwright (not a hard failure). Troubleshooting hangs / CDP:
PyScript headless debug above.
| Script | Purpose |
|---|---|
run_desktop_lv_tests.py |
LVGL desktop matrix (sync/async, strict clicks) |
lv_timer_test_kit.py |
Full LVGL timer matrix (sync/async, all interpreters) |
run_test_timers.py |
Run the sibling core multimer timer probe across desktop interpreters |
multimer_source_preload.py |
Force one multimer wake source, then run a script |
Comparing multimer wake sources: lv_timer_test_kit.py --source NAME (or
example_test_kit.py with MULTIMER_SOURCE set, which forwards
--multimer-source to the wrapper). Both set MULTIMER_SOURCE inside the
child and make multimer choose its source before the script runs, so they also
work for the Windows .exe interpreters, which cannot read WSL-exported env
vars. An interpreter that can't start that source reports unavailable and
doesn't fail the run. MULTIMER_BACKEND is retired: the kit refuses to run
with it set. The sources, and which host uses which, are in pydevices'
multimer hosts table.
TestPyPI package smoke tests are owned by the repositories that publish the
packages: core checks live in
pydevices/tools/test_testpypi_standalone.sh,
and pygraphics has its own standalone wheel check.
| Script | Purpose |
|---|---|
quit_inject.py |
Inject quit into running examples (used by the example harness) |
pydevices_test_mode.py |
Test-mode env for examples |
test_gallery_audio.py |
Load the audio cards (Audio Arpeggio, Piano, Drum Machine, Audiolive Rack) on the direct-WebAssembly gallery in headless Chromium, click as a visitor would, and assert non-silent audio reaches Web Audio |
screenshot.py |
Run a desktop example and save its SDL2/pygame-ce window as PNG |
record.py |
Run a desktop example and record its SDL2/pygame-ce window with FFmpeg |
typings/ |
MicroPython stdlib stubs + core package .pyi (see below) |
python tools/screenshot.py hello.py
python tools/screenshot.py bouncing_balls 3
python tools/screenshot.py logo --delay 2 --resolution 320x240 --scale 1Without --output, screenshots are saved as
docs/screenshots/EXAMPLE_NAME.png.
python tools/record.py bouncing_balls
python tools/record.py bouncing_balls 10
python tools/record.py logo --duration 3 --fps 15 --resolution 320x240 --scale 1Without --output, recordings are saved as
docs/videos/EXAMPLE_NAME.mp4. Recording requires ffmpeg on PATH or
the binary-bundled imageio-ffmpeg Python package.
stubPath for Pylance / pyright (.vscode/settings.json, pyrightconfig.json):
| Content | Source |
|---|---|
| MicroPython stdlib stubs | committed under tools/typings/ |
displaydev / appdev / multimer / events / keys |
committed package trees / modules; regenerate with ../scripts/gen_package_pyi.sh |
lvgl |
committed tools/typings/lvgl.pyi (from ../lvgl-bindings/generated/lvgl.pyi) |
Confirm Python: Select Interpreter → .venv/bin/python. Cursor uses cursorpyright with stubPath / typeshedPaths → tools/typings (configured in a local .vscode/settings.json when present).