Skip to content

Commit a81c18f

Browse files
hoodmanedavidhewitt
authored andcommitted
Add CI tests in Pyodide
1 parent 6347ad4 commit a81c18f

File tree

8 files changed

+201
-2
lines changed

8 files changed

+201
-2
lines changed

.github/workflows/ci.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ on:
33
push:
44
branches:
55
- main
6+
- emscripten-ci2
67
pull_request:
78

89
concurrency:
@@ -350,3 +351,52 @@ jobs:
350351
CIBW_BUILD_VERBOSITY: 1
351352
with:
352353
package-dir: examples/namespace_package
354+
355+
test-emscripten:
356+
name: Test Emscripten
357+
runs-on: ubuntu-latest
358+
steps:
359+
- uses: actions/checkout@v3
360+
- uses: actions/setup-node@v3
361+
with:
362+
node-version: 18
363+
- run: |
364+
PYODIDE_VERSION=0.21.0-alpha.2
365+
366+
cd emscripten
367+
npm i pyodide@0.21.0-alpha.2 prettier
368+
cd node_modules/pyodide/
369+
node ../prettier/bin-prettier.js -w pyodide.asm.js
370+
EMSCRIPTEN_VERSION=$(node -p "require('./repodata.json').info.platform.split('_').slice(1).join('.')")
371+
PYTHON_VERSION=3.10.2
372+
373+
echo "PYODIDE_VERSION=$PYODIDE_VERSION" >> $GITHUB_ENV
374+
echo "EMSCRIPTEN_VERSION=$EMSCRIPTEN_VERSION" >> $GITHUB_ENV
375+
echo "PYTHON_VERSION=$PYTHON_VERSION" >> $GITHUB_ENV
376+
echo "ORIG_PATH=$PATH" >> $GITHUB_ENV
377+
- uses: actions-rs/toolchain@v1
378+
with:
379+
profile: minimal
380+
toolchain: nightly
381+
components: rust-src
382+
target: wasm32-unknown-emscripten
383+
override: true
384+
- uses: mymindstorm/setup-emsdk@v11
385+
with:
386+
version: ${{env.EMSCRIPTEN_VERSION}}
387+
actions-cache-folder: emsdk-cache
388+
- uses: actions/setup-python@v2
389+
id: setup-python
390+
with:
391+
python-version: ${{env.PYTHON_VERSION}}
392+
- run: pip install nox
393+
- uses: actions/cache@v3
394+
with:
395+
path: |
396+
tests/pyodide
397+
key: ${{ hashFiles('tests/*.js') }} - ${{ hashFiles('noxfile.py') }} - ${{ steps.setup-python.outputs.python-path }}
398+
- uses: Swatinem/rust-cache@v1
399+
- name: Test
400+
run: |
401+
export PATH=$ORIG_PATH:$PATH
402+
nox -s test-examples-emscripten

emscripten/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
builddir
2+
main.*
3+
!main.c
4+
pybuilddir.txt
5+
pyodide
6+
node_modules
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# system configuration generated and used by the sysconfig module
2+
build_time_vars = {
3+
"ABIFLAGS": "",
4+
"AR": "/src/emsdk/emsdk/upstream/emscripten/emar",
5+
"ARFLAGS": "rcs",
6+
"BLDSHARED": "emcc -sSIDE_MODULE=1 -L/src/emscripten/python-lib/",
7+
"CC": "emcc -I/src/emscripten/python-include/",
8+
"CCSHARED": "",
9+
"CFLAGS": "-Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g "
10+
"-fwrapv -O3 -Wall -O2 -g0 -fPIC",
11+
"EXT_SUFFIX": ".cpython-310-wasm32-emscripten.so",
12+
"HOST_GNU_TYPE": "wasm32-unknown-emscripten",
13+
"LDSHARED": "emcc -sSIDE_MODULE=1",
14+
"Py_DEBUG": "0",
15+
"py_version_nodot": "310",
16+
}

emscripten/emcc_wrapper.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python3
2+
import subprocess
3+
import sys
4+
5+
6+
def update_args(args):
7+
# remove -lc. Not sure if it makes a difference but -lc doesn't belong here.
8+
# https://github.com/emscripten-core/emscripten/issues/17191
9+
for i in reversed(range(len(args))):
10+
if args[i] == "c" and args[i - 1] == "-l":
11+
del args[i - 1 : i + 1]
12+
13+
return args
14+
15+
16+
def main(args):
17+
args = update_args(args)
18+
return subprocess.call(["emcc"] + args)
19+
20+
21+
if __name__ == "__main__":
22+
args = sys.argv[1:]
23+
sys.exit(main(args))

emscripten/pyo3_config.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
implementation=CPython
2+
version=3.10
3+
shared=true
4+
abi3=false
5+
lib_name=python3.10
6+
pointer_width=32
7+
suppress_build_script_link_lines=false

emscripten/runner.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const { opendir } = require("node:fs/promises");
2+
const { loadPyodide } = require("pyodide");
3+
4+
async function findWheel(distDir) {
5+
const dir = await opendir(distDir);
6+
for await (const dirent of dir) {
7+
if (dirent.name.endsWith("whl")) {
8+
return dirent.name;
9+
}
10+
}
11+
}
12+
13+
const pkgDir = process.argv[2];
14+
const distDir = pkgDir + "/dist";
15+
const testDir = pkgDir + "/tests";
16+
17+
async function main() {
18+
const wheelName = await findWheel(distDir);
19+
const wheelURL = `file:${distDir}/${wheelName}`;
20+
21+
try {
22+
pyodide = await loadPyodide();
23+
const FS = pyodide.FS;
24+
const NODEFS = FS.filesystems.NODEFS;
25+
FS.mkdir("/test_dir");
26+
FS.mount(NODEFS, { root: testDir }, "/test_dir");
27+
await pyodide.loadPackage(["micropip", "pytest", "tomli"]);
28+
const micropip = pyodide.pyimport("micropip");
29+
await micropip.install(wheelURL);
30+
const pytest = pyodide.pyimport("pytest");
31+
errcode = pytest.main(pyodide.toPy(["/test_dir", "-vv"]));
32+
} catch (e) {
33+
console.error(e);
34+
process.exit(1);
35+
}
36+
}
37+
38+
main();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sys
2+
import pytest
3+
4+
if sys.platform == "emscripten":
5+
6+
@pytest.fixture
7+
def benchmark():
8+
def result(func, *args, **kwargs):
9+
return func(*args, **kwargs)
10+
11+
return result

noxfile.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import nox
12
import os
3+
import sys
24
import tarfile
35
from glob import glob
46
from pathlib import Path
57

6-
import nox
7-
88

99
@nox.session(name="test-examples", venv_backend="none")
1010
def test_examples(session: nox.Session):
@@ -35,3 +35,51 @@ def mypy(session: nox.Session):
3535
def test(session: nox.Session):
3636
session.install("pytest", ".")
3737
session.run("pytest", "setuptools_rust", *session.posargs)
38+
39+
40+
@nox.session(name="test-examples-emscripten")
41+
def test_examples_emscripten(session: nox.Session):
42+
session.install(".")
43+
emscripten_dir = Path("./emscripten").resolve()
44+
45+
session.run(
46+
"rustup",
47+
"component",
48+
"add",
49+
"rust-src",
50+
"--toolchain",
51+
"nightly",
52+
external=True,
53+
)
54+
examples_dir = Path("examples")
55+
test_crates = [
56+
examples_dir / "html-py-ever",
57+
examples_dir / "namespace_package",
58+
]
59+
for example in test_crates:
60+
env = os.environ.copy()
61+
env.update(
62+
RUSTUP_TOOLCHAIN="nightly",
63+
_SETUPTOOLSRUST_BUILD_STD="1",
64+
PYTHONPATH=str(emscripten_dir),
65+
_PYTHON_SYSCONFIGDATA_NAME="_sysconfigdata__emscripten_wasm32-emscripten",
66+
_PYTHON_HOST_PLATFORM="emscripten_3_1_14_wasm32",
67+
CARGO_BUILD_TARGET="wasm32-unknown-emscripten",
68+
CARGO_UNSTABLE_BUILD_STD="true",
69+
CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_LINKER=str(
70+
emscripten_dir / "emcc_wrapper.py"
71+
),
72+
PYO3_CONFIG_FILE=str(emscripten_dir / "pyo3_config.ini"),
73+
RUSTFLAGS=" ".join(
74+
[
75+
"-C relocation-model=pic",
76+
"-C link-arg=-sSIDE_MODULE=2",
77+
"-C link-arg=-sWASM_BIGINT",
78+
]
79+
),
80+
)
81+
with session.chdir(example):
82+
session.run("python", "setup.py", "bdist_wheel", env=env, external=True)
83+
84+
with session.chdir(emscripten_dir):
85+
session.run("node", "runner.js", str(example), external=True)

0 commit comments

Comments
 (0)