From b0a08a3f0b75a3d35b2aa46396f8e80fffa489e0 Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Sun, 16 Aug 2026 18:34:33 -0400 Subject: [PATCH] Add optional Pyodide wheels Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com> --- copier.yaml | 6 ++++ examples/cpp.yaml | 1 + examples/rust.yaml | 1 + python/cpp/.github/workflows/build.yaml.jinja | 33 +++++++++++++++++++ python/cpp/.gitignore.jinja | 3 ++ python/cpp/Makefile.jinja | 6 +++- python/cpp/pyproject.toml.jinja | 11 +++++-- ...add_pyodide %}__init__.py{% endif %}.jinja | 1 + .../rust/.github/workflows/build.yaml.jinja | 28 ++++++++++++++++ python/rust/.gitignore.jinja | 3 ++ python/rust/Makefile.jinja | 7 +++- python/rust/pyproject.toml.jinja | 12 +++++-- .../rust/{{module}}/tests/test_all.py.jinja | 4 +-- ...add_pyodide %}__init__.py{% endif %}.jinja | 1 + 14 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 python/cpp/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja create mode 100644 python/rust/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja diff --git a/copier.yaml b/copier.yaml index 5cce84c..09b6dc7 100644 --- a/copier.yaml +++ b/copier.yaml @@ -71,6 +71,12 @@ add_vcpkg: help: Add vcpkg dependency management when: "{{ add_extension in ['cpp', 'cppjswasm'] }}" +add_pyodide: + type: bool + default: false + help: Build and test Pyodide wheels + when: "{{ add_extension in ['cpp', 'rust'] }}" + python_version_primary: type: str default: 3.11 diff --git a/examples/cpp.yaml b/examples/cpp.yaml index 71b927d..f22ab39 100644 --- a/examples/cpp.yaml +++ b/examples/cpp.yaml @@ -1,5 +1,6 @@ --- add_docs: true +add_pyodide: false add_vcpkg: true add_wiki: true add_extension: cpp diff --git a/examples/rust.yaml b/examples/rust.yaml index 963ed99..6fad72c 100644 --- a/examples/rust.yaml +++ b/examples/rust.yaml @@ -1,5 +1,6 @@ --- add_docs: true +add_pyodide: false add_wiki: true add_extension: rust email: 3105306+timkpaine@users.noreply.github.com diff --git a/python/cpp/.github/workflows/build.yaml.jinja b/python/cpp/.github/workflows/build.yaml.jinja index 00a6f2f..38a9b21 100644 --- a/python/cpp/.github/workflows/build.yaml.jinja +++ b/python/cpp/.github/workflows/build.yaml.jinja @@ -161,3 +161,36 @@ jobs: with: name: {% raw %}dist-${{ matrix.os }}-${{ matrix.python.version }}{% endraw %} path: dist + +{% if add_pyodide %} + pyodide: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup Python + uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 + with: + version: "3.14" + + - name: Setup C++ + uses: actions-ext/cpp/setup@c2b1e90366b3a1c628a1df0748135f5e345ab26f + + {% if add_vcpkg -%} + - name: Setup vcpkg cache + uses: actions-ext/cpp/setup-vcpkg-cache@c2b1e90366b3a1c628a1df0748135f5e345ab26f + + {% endif -%} + - name: Build and test Pyodide wheel + run: make test-pyodide + + - name: Upload Pyodide distribution + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: dist-pyodide + path: dist/pyodide +{% endif %} diff --git a/python/cpp/.gitignore.jinja b/python/cpp/.gitignore.jinja index 0be369b..238c203 100644 --- a/python/cpp/.gitignore.jinja +++ b/python/cpp/.gitignore.jinja @@ -148,6 +148,9 @@ Untitled*.ipynb # Emscripten SDK (locally installed) emsdk +{% if add_pyodide -%} +.pyodide_build/ +{% endif %} # Mac .DS_Store diff --git a/python/cpp/Makefile.jinja b/python/cpp/Makefile.jinja index 9e7b8c4..53e14ac 100644 --- a/python/cpp/Makefile.jinja +++ b/python/cpp/Makefile.jinja @@ -84,8 +84,12 @@ tests-py: test-py coverage-py: ## run python tests and collect test coverage python -m pytest -v {{ module }}/tests --cov={{ module }} --cov-report term-missing --cov-report xml -.PHONY: test coverage tests +.PHONY: test{% if add_pyodide %} test-pyodide{% endif %} coverage tests test: test-py ## run all tests +{% if add_pyodide %} +test-pyodide: ## build and test the Python package in Pyodide + uvx --from cibuildwheel==4.2.0 cibuildwheel --only cp314-pyodide_wasm32 --output-dir dist/pyodide . +{% endif %} coverage: coverage-py ## run all tests and collect test coverage # alias diff --git a/python/cpp/pyproject.toml.jinja b/python/cpp/pyproject.toml.jinja index 4273f6a..1f72ba0 100644 --- a/python/cpp/pyproject.toml.jinja +++ b/python/cpp/pyproject.toml.jinja @@ -1,7 +1,7 @@ [build-system] requires = [ "hatchling", - "hatch-cpp", + "hatch-cpp{% if add_pyodide %}>=0.5.0{% endif %}", "pybind11", ] build-backend = "hatchling.build" @@ -46,7 +46,7 @@ develop = [ "check-dist", "cibuildwheel", "codespell", - "hatch-cpp", + "hatch-cpp{% if add_pyodide %}>=0.5.0{% endif %}", "hatchling", "mdformat", "mdformat-tables>=1", @@ -100,6 +100,13 @@ archs = "arm64" archs = "AMD64" skip = "*win32 *arm_64" +{% if add_pyodide -%} +[tool.cibuildwheel.pyodide] +test-command = "python -m pytest -vvv --pyargs {{ module }}.tests" +test-extras = [] +test-requires = ["pytest"] +{% endif -%} + [tool.coverage.run] branch = true omit = [ diff --git a/python/cpp/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja b/python/cpp/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/python/cpp/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja @@ -0,0 +1 @@ + diff --git a/python/rust/.github/workflows/build.yaml.jinja b/python/rust/.github/workflows/build.yaml.jinja index dbb9c99..0814ef8 100644 --- a/python/rust/.github/workflows/build.yaml.jinja +++ b/python/rust/.github/workflows/build.yaml.jinja @@ -135,3 +135,31 @@ jobs: with: name: {% raw %}dist-${{ matrix.os }}{% endraw %} path: dist + +{% if add_pyodide %} + pyodide: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup Python + uses: actions-ext/python/setup@6e1b91a408ea89f49bc8aff8724f83826f7b5446 + with: + version: "3.14" + + - name: Setup Rust + uses: actions-ext/rust/setup@2db8e3e4c56beb7320c7d730bd2c2d6a6447bf6b + + - name: Build and test Pyodide wheel + run: make test-pyodide + + - name: Upload Pyodide distribution + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: dist-pyodide + path: dist/pyodide +{% endif %} diff --git a/python/rust/.gitignore.jinja b/python/rust/.gitignore.jinja index f57991a..c980a45 100644 --- a/python/rust/.gitignore.jinja +++ b/python/rust/.gitignore.jinja @@ -142,6 +142,9 @@ Untitled*.ipynb # Emscripten SDK (locally installed) emsdk +{% if add_pyodide -%} +.pyodide_build/ +{% endif %} # Mac .DS_Store diff --git a/python/rust/Makefile.jinja b/python/rust/Makefile.jinja index 9d658da..1201c95 100644 --- a/python/rust/Makefile.jinja +++ b/python/rust/Makefile.jinja @@ -110,8 +110,13 @@ tests-rs: test-rs coverage-rs: ## run rust tests and collect test coverage make -C rust coverage -.PHONY: test coverage tests +.PHONY: test{% if add_pyodide %} test-pyodide{% endif %} coverage tests test: test-py test-rs ## run all tests +{% if add_pyodide %} +test-pyodide: ## build and test the Python package in Pyodide + rustup target add wasm32-unknown-emscripten + uvx --from cibuildwheel==4.2.0 cibuildwheel --only cp314-pyodide_wasm32 --output-dir dist/pyodide . +{% endif %} coverage: coverage-py coverage-rs ## run all tests and collect test coverage # alias diff --git a/python/rust/pyproject.toml.jinja b/python/rust/pyproject.toml.jinja index dcdec3f..0be59f9 100644 --- a/python/rust/pyproject.toml.jinja +++ b/python/rust/pyproject.toml.jinja @@ -1,7 +1,7 @@ [build-system] requires = [ "hatchling", - "hatch-rs>=0.1.4", + "hatch-rs>={% if add_pyodide %}0.3.0{% else %}0.1.4{% endif %}", ] build-backend = "hatchling.build" @@ -46,7 +46,7 @@ develop = [ "check-dist", "cibuildwheel", "codespell", - "hatch-rs", + "hatch-rs{% if add_pyodide %}>=0.3.0{% endif %}", "hatchling", "mdformat", "mdformat-tables>=1", @@ -118,6 +118,14 @@ archs = "arm64" environment = {PATH="$UserProfile\\.cargo\bin;$PATH", CARGO_TERM_COLOR="always"} skip = "*win32 *arm_64" +{% if add_pyodide -%} +[tool.cibuildwheel.pyodide] +test-command = "python -m pytest -vvv --pyargs {{ module }}.tests" +test-extras = [] +test-requires = ["pytest"] +xbuild-tools = ["cargo", "rustc", "rustup"] +{% endif -%} + [tool.coverage.run] branch = true omit = [ diff --git a/python/rust/{{module}}/tests/test_all.py.jinja b/python/rust/{{module}}/tests/test_all.py.jinja index 152fb40..1c802c0 100644 --- a/python/rust/{{module}}/tests/test_all.py.jinja +++ b/python/rust/{{module}}/tests/test_all.py.jinja @@ -1,5 +1,5 @@ -from {{ module }} import * +from {{ module }}{% if add_pyodide %}.{{ module }} import Example{% else %} import *{% endif %} def test_all(): - assert True + assert {% if add_pyodide %}str(Example("test")) == "test"{% else %}True{% endif %} diff --git a/python/rust/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja b/python/rust/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/python/rust/{{module}}/tests/{% if add_pyodide %}__init__.py{% endif %}.jinja @@ -0,0 +1 @@ +