QuantEcon / QuantEcon/QuantEcon.py

WASM: add a WebAssembly/JupyterLite smoke-test job to CI

Open
#933 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

CI infrastructure
Dominant language
Python
Stars
2.4k
Forks
2.3k
Avg merge
3d 3h
Merged PRs (30d)
3

Description

[!NOTE]
Updated 2026-08-21. The seed is now on main via #938 (ci/wasm/environment.yml, ci/wasm/smoke_test.py, the Playwright harness ci/wasm/test_jupyterlite.py, and the native gate in ci_wasm_smoke.yml), so the Approach below names it instead of "the config from #928", and a task list for the runner PR has been added. What that list corrects: the parked job (2026-08-19 comment) still needs triggers, the gini expectation in both suite files is inverted now that numba 0.67.0 build 1 serialises parallel=True (patch 0010, emscripten-forge/recipes#6293), the two jitted-generator tests will hang without a real guard, test_simplex_grid fails on a warm cache (#944), and smoke_test.py is not yet shipped into the kernel. The original text is preserved in the edit history.

Part of #925 (Phase 3 — keep it working).

Goal

Browser support must not regress once established: add a wasm job to GitHub Actions that runs a smoke suite in the real browser stack.

Approach

  • Seed: PR #938 (merged 2026-08-21) — ci/wasm/environment.yml (xeus-python kernel env, emscripten-forge-4x + conda-forge channels), the sentinel-driven Playwright harness ci/wasm/test_jupyterlite.py, and the native gate job native in .github/workflows/ci_wasm_smoke.yml, which runs ci/wasm/smoke_test.py against repo source on every PR. The Emscripten runner job is parked in the 2026-08-19 comment below.
  • Execute in the xeus-python kernel via headless Chromium + Playwright (the route #938 chose). pytester, emscripten-forge's pyjs-based recipe-test harness, remains the alternative only if quantecon is later packaged on emscripten-forge (#932).
  • pandas and pytest are both packaged for WASM, so meaningful portions of the real test suite can eventually run in-browser — start with the smoke list from #928 (smoke_test.py) and grow from there.
  • Track the emscripten-forge numba/llvmlite versions in the job the way conda-forge versions are tracked today; environment.yml pins bare numba, so have the job log the resolved build (only 0.67.0 build 1 carries patch 0010, see task 2).

This job is also the natural watchpoint for upstreaming: the blog post signals intent to merge the WASM engine into Numba mainline, and once that ships, the patched-recipe caveats (the recipe now carries ten patches, e.g. the serial fallback for parallel=True added by patch 0010 and the ufunc caching disabled by patch 0007) may start dissolving. Until then the job tests the released conda-forge noarch quantecon, not repo source, so per-PR triggering is not useful long-term.

Tasks for the runner PR

  1. Add the parked wasm job with its own triggers. The snippet in the 2026-08-19 comment is a job block and has no on:; ci_wasm_smoke.yml's on: is push/pull_request to main. Either add schedule + workflow_dispatch to that shared on: block and guard the job with if: github.event_name != 'pull_request', or move it to its own workflow file — in which case needs: native no longer resolves (it only sees jobs in the same workflow) and must be dropped or replaced. Iterate under pull_request first, as the 2026-08-19 comment describes, then flip.
  2. Flip the gini expectation. numba 0.67.0 build 1 on emscripten-forge-4x (patch 0010, emscripten-forge/recipes#6293, merged 2026-08-18) demotes @njit(parallel=True) to the serial pipeline, and the bare numba pin resolves to it. test_gini_coefficient's xfail(IS_EMSCRIPTEN, strict=True) in smoke_test.py would XPASS and fail; test_gini_fails_on_emscripten in test_jupyterlite.py asserts an error and would fail. Both become plain pass assertions (browser-unverified; inferred from the merged patch). PR #937's O(n log n) rewrite (#926) is a performance change on top of this, not a prerequisite.
  3. Guard the two jitted-generator tests. test_support_enumeration and test_vertex_enumeration in smoke_test.py call @njit generators (support_enumeration.py:96, vertex_enumeration.py:118) that never return on Emscripten (#927, https://github.com/QuantEcon/QuantEcon.py/issues/927#issuecomment-5338104706). Use pytest.mark.skipif(IS_EMSCRIPTEN, ...) or xfail(IS_EMSCRIPTEN, run=False, strict=True) — a plain xfail still executes the test, and pytest-timeout (SIGALRM / threading.Timer) cannot interrupt a native loop in the single-threaded kernel. In test_jupyterlite.py, skip test_support_enumeration or run it last: Playwright's EXEC_MS (180 s) bounds it host-side, but the module-scoped kernel stays wedged and the following test fails spuriously. Add the minimal bare @njit generator test (for i in range(n): yield i) alongside, guarded the same way, as the isolated upstream repro and regression watch.
  4. Mark test_simplex_grid as expected to fail on a warm cache and cite #944. Its comment currently says "(#929)"; the actual failure is RuntimeError: no compiled object yet for comb_jit (#944, reported upstream as emscripten-forge/recipes#6309), which triggers only when a cache=True caller links a callee restored from the persistent cache — a cold single-session run passes. A regression test needs a second session or a pre-seeded /drive/.cache/numba.
  5. Ship smoke_test.py into the kernel and run it in-kernel. The parked job runs only the Playwright harness (6 cells: kernel boot, import, tauchen, np.linalg.solve, support_enumeration, gini), so a green run fills at most 5 of #928's 13 rows. #938 assigned in-kernel pytest execution of smoke_test.py (16 tests) here (commit 1e0c598 also dropped pytest from environment.yml, so it has to be added back). Copy the file into the site contents, invoke pytest inside the xeus-python kernel, and surface per-test results.
  6. Bump mamba-org/setup-micromamba@v2 to @v3 in the parked YAML (v3.0.0 moved to Node 24; the inputs used — micromamba-binary-path, init-shell, generate-run-shell — are unchanged). actions/checkout@v7 and actions/setup-python@v7 are current.

Expected outcome of the first browser run once 1–6 are done, on numba 0.67.0 build 1: test_gini_coefficient passes; test_support_enumeration, test_vertex_enumeration and the bare-generator test are skipped/xfail(run=False) pending the upstream generator report (#927); test_simplex_grid passes cold and fails warm (#944); everything else is expected to pass. Record the per-function table in #928.

Acceptance criteria

  • A CI job builds a JupyterLite deployment containing quantecon and runs the smoke suite in a headless browser
  • ci/wasm/smoke_test.py executes inside the xeus-python kernel (not only the Playwright harness), so every #928 row gets a browser result
  • Failures are actionable — per-function results rather than a monolithic pass/fail
  • Known in-browser failures (#927 generators, #944 warm-cache comb_jit) are guarded so they cannot consume the job's time limit, and each guard cites its issue

Related

  • #938 — seed (merged 2026-08-21): ci/wasm/environment.yml, smoke_test.py, test_jupyterlite.py, native gate
  • #928 — results table this job fills; smoke_test.py is its checklist
  • #927 and https://github.com/QuantEcon/QuantEcon.py/issues/927#issuecomment-5338104706 — jitted-generator hang (support_enumeration, vertex_enumeration); LAPACK verified working
  • #944 — cache=True callers of cache-restored functions fail on a warm cache (simplex_gridnum_compositions_jitcomb_jit); upstream emscripten-forge/recipes#6309
  • #926 / PR #937 — gini O(n log n) rewrite, approved and awaiting merge
  • emscripten-forge/recipes#6293 — patch 0010, serial fallback for parallel=True (numba 0.67.0 build 1)
  • #932 — if quantecon is packaged on emscripten-forge, this job switches channel and pytester becomes an option

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with .github/workflows/ci_wasm_smoke.yml and the existing files ci/wasm/environment.yml, ci/wasm/smoke_test.py, and ci/wasm/test_jupyterlite.py. Run the native smoke gate first, then inspect the parked runner job and the referenced tests and upstream issues. Done means a browser CI job runs the in-kernel smoke suite with per-function results and guards the documented failures.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, jupyter, playwright, python, wasm
Domain
ci-cd, devops, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.