feat: pyemscripten platform tag (PEP 783), 314.0a1, pyodide-eol flag, and maintenance updates (#2812)

* Use `pyodide config get emscripten_dir`

* Update constraints

* Updates for `pyemscripten` platform tag

* Weird constraints stuff...

* Revert "Weird constraints stuff..."

This reverts commit 4bd785de872269c0b27362ee800f17288b2b4789.

* Add some special constraints for pyodide

* Wheel pyemscripten collision

* Put delocate back in constraints (not needed tho)

* Drop Pyodide 0.27.7 (cp312-pyodide_wasm32)

* Add Pyodide 314.0.0a1 (cp314-pyodide_wasm32)

* Delete no-longer-needed Pyodide 312 constraints

* Update Pyodide 313 and 314 constraints

* Add back prerelease stuff and update tests

* Fix `test_pyodide_on_windows`

* Update and fix tests some more

* Also enable pyodide-prerelease in GHA sample build

* Update tests yet again (`SINGLE_PYTHON_VERSION` changes)

* Add a Pyodide-specific maintenance guide

* Add suggestions from Hood and Gyeongjae

Co-Authored-By: Hood Chatham <roberthoodchatham@gmail.com>
Co-Authored-By: Gyeongjae Choi <def6488@gmail.com>

* Add "Last updated: May 2026" to Pyodide document

* Add a Pyodide EOL filter

* Fix typo

* Update maintenance docs about EoL Pyodide

* Add Pyodide 312 constraints back

* Update Pyodide 313 and 314 constraints

* Remove "experimental" note about Pyodide

* Remove another experimental Pyodide sentence

* Add docs about the `pyodide-eol` enable option

---------

Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Gyeongjae Choi <def6488@gmail.com>
Co-authored-by: Henry Schreiner <henryfs@princeton.edu>
This commit is contained in:
agriya khetarpal
2026-05-09 14:01:41 -04:00
committed by GitHub
co-authored by Hood Chatham Gyeongjae Choi Henry Schreiner
parent 542335fb4a
commit d2c0e59833
20 changed files with 343 additions and 99 deletions
+2
View File
@@ -321,11 +321,13 @@ jobs:
output-dir: wheelhouse
env:
CIBW_PLATFORM: pyodide
CIBW_ENABLE: pyodide-prerelease
- name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
run: uv run --no-sync ./bin/run_tests.py
env:
CIBW_PLATFORM: pyodide
CIBW_ENABLE: pyodide-prerelease
test-uv-extras:
name: Test uv extra on ${{ matrix.os }} ${{ matrix.test_select }}
+10 -11
View File
@@ -94,20 +94,19 @@ def ensure_node(major_version: str) -> Path:
return path
def install_emscripten(
env: dict[str, str], version: str, xbuildenv_cache_path: Path, pyodide_version: str
) -> Path:
def install_emscripten(env: dict[str, str], version: str, xbuildenv_cache_path: Path) -> Path:
"""Install Emscripten via pyodide-build, which also applies Pyodide-specific patches."""
emcc_path = (
xbuildenv_cache_path / pyodide_version / "emsdk" / "upstream" / "emscripten" / "emcc"
emscripten_dir = Path(
call("pyodide", "config", "get", "emscripten_dir", env=env, capture_stdout=True).strip()
)
with FileLock(CIBW_CACHE_PATH / "emscripten.lock"):
if emcc_path.exists():
return emcc_path
if emscripten_dir.exists():
return emscripten_dir
call(
"pyodide",
"xbuildenv",
"install-emscripten",
"--force",
"--version",
version,
"--path",
@@ -115,8 +114,8 @@ def install_emscripten(
env=env,
cwd=CIBW_CACHE_PATH,
)
assert emcc_path.exists()
return emcc_path
assert emscripten_dir.exists()
return emscripten_dir
def get_all_xbuildenv_version_info(env: dict[str, str]) -> list[PyodideXBuildEnvInfo]:
@@ -315,9 +314,9 @@ def setup_python(
log.step(
f"Installing Emscripten {emscripten_version} and applying Pyodide-specific patches ..."
)
emcc_path = install_emscripten(env, emscripten_version, xbuildenv_cache_path, pyodide_version)
emscripten_dir = install_emscripten(env, emscripten_version, xbuildenv_cache_path)
env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]])
env["PATH"] = os.pathsep.join([str(emscripten_dir), env["PATH"]])
return env
+2 -1
View File
@@ -206,7 +206,8 @@ python_configurations = [
[pyodide]
python_configurations = [
{ identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.7", node_version = "v22" },
{ identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.29.3", node_version = "v22" },
{ identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.29.4", node_version = "v22" },
{ identifier = "cp314-pyodide_wasm32", version = "3.14", default_pyodide_version = "314.0.0a1", node_version = "v24" },
]
[android]
@@ -2,15 +2,15 @@
# nox -s update_constraints
annotated-types==0.7.0
# via pydantic
auditwheel-emscripten==0.2.3
auditwheel-emscripten==0.2.4
# via pyodide-build
build==1.2.2.post1
build==1.5.0
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.2.25
certifi==2026.4.22
# via requests
charset-normalizer==3.4.6
charset-normalizer==3.4.7
# via requests
click==8.1.8
# via
@@ -19,54 +19,54 @@ click==8.1.8
# pyodide-cli
distlib==0.4.0
# via virtualenv
filelock==3.25.2
filelock==3.29.0
# via
# python-discovery
# virtualenv
idna==3.11
idna==3.13
# via requests
leb128==1.0.9
# via auditwheel-emscripten
markdown-it-py==4.0.0
markdown-it-py==4.2.0
# via rich
mdurl==0.1.2
# via markdown-it-py
packaging==26.0
packaging==26.2
# via
# auditwheel-emscripten
# build
# pyodide-build
# wheel
pip==26.0.1
pip==26.1.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.4
platformdirs==4.9.6
# via
# pyodide-build
# python-discovery
# virtualenv
pydantic==2.12.5
pydantic==2.13.4
# via
# pyodide-build
# pyodide-lock
pydantic-core==2.41.5
pydantic-core==2.46.4
# via pydantic
pygments==2.19.2
pygments==2.20.0
# via rich
pyodide-build==0.33.0
pyodide-build==0.34.3
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
# auditwheel-emscripten
# pyodide-build
pyodide-lock==0.1.2
pyodide-lock==0.1.3
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
python-discovery==1.3.0
# via virtualenv
requests==2.32.5
requests==2.33.1
# via pyodide-build
rich==14.3.3
rich==15.0.0
# via
# pyodide-build
# pyodide-cli
@@ -81,11 +81,11 @@ typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.2.0
virtualenv==21.3.1
# via
# build
# pyodide-build
wheel==0.46.3
wheel==0.47.0
# via
# auditwheel-emscripten
# pyodide-build
@@ -2,15 +2,15 @@
# nox -s update_constraints
annotated-types==0.7.0
# via pydantic
auditwheel-emscripten==0.2.3
auditwheel-emscripten==0.2.4
# via pyodide-build
build==1.2.2.post1
build==1.5.0
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.2.25
certifi==2026.4.22
# via requests
charset-normalizer==3.4.6
charset-normalizer==3.4.7
# via requests
click==8.1.8
# via
@@ -19,54 +19,54 @@ click==8.1.8
# pyodide-cli
distlib==0.4.0
# via virtualenv
filelock==3.25.2
filelock==3.29.0
# via
# python-discovery
# virtualenv
idna==3.11
idna==3.13
# via requests
leb128==1.0.9
# via auditwheel-emscripten
markdown-it-py==4.0.0
markdown-it-py==4.2.0
# via rich
mdurl==0.1.2
# via markdown-it-py
packaging==26.0
packaging==26.2
# via
# auditwheel-emscripten
# build
# pyodide-build
# wheel
pip==26.0.1
pip==26.1.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.4
platformdirs==4.9.6
# via
# pyodide-build
# python-discovery
# virtualenv
pydantic==2.12.5
pydantic==2.13.4
# via
# pyodide-build
# pyodide-lock
pydantic-core==2.41.5
pydantic-core==2.46.4
# via pydantic
pygments==2.19.2
pygments==2.20.0
# via rich
pyodide-build==0.33.0
pyodide-build==0.34.3
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
# auditwheel-emscripten
# pyodide-build
pyodide-lock==0.1.2
pyodide-lock==0.1.3
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
python-discovery==1.3.0
# via virtualenv
requests==2.32.5
requests==2.33.1
# via pyodide-build
rich==14.3.3
rich==15.0.0
# via
# pyodide-build
# pyodide-cli
@@ -81,11 +81,11 @@ typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.2.0
virtualenv==21.3.1
# via
# build
# pyodide-build
wheel==0.46.3
wheel==0.47.0
# via
# auditwheel-emscripten
# pyodide-build
@@ -0,0 +1,91 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
annotated-types==0.7.0
# via pydantic
auditwheel-emscripten==0.2.4
# via pyodide-build
build==1.5.0
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.4.22
# via requests
charset-normalizer==3.4.7
# via requests
click==8.1.8
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
# pyodide-cli
distlib==0.4.0
# via virtualenv
filelock==3.29.0
# via
# python-discovery
# virtualenv
idna==3.13
# via requests
leb128==1.0.9
# via auditwheel-emscripten
markdown-it-py==4.2.0
# via rich
mdurl==0.1.2
# via markdown-it-py
packaging==26.2
# via
# auditwheel-emscripten
# build
# pyodide-build
# wheel
pip==26.1.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.6
# via
# pyodide-build
# python-discovery
# virtualenv
pydantic==2.13.4
# via
# pyodide-build
# pyodide-lock
pydantic-core==2.46.4
# via pydantic
pygments==2.20.0
# via rich
pyodide-build==0.34.3
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
# auditwheel-emscripten
# pyodide-build
pyodide-lock==0.1.3
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.3.0
# via virtualenv
requests==2.33.1
# via pyodide-build
rich==15.0.0
# via
# pyodide-build
# pyodide-cli
ruamel-yaml==0.19.1
# via pyodide-build
typing-extensions==4.15.0
# via
# pydantic
# pydantic-core
# typing-inspection
typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.3.1
# via
# build
# pyodide-build
wheel==0.47.0
# via
# auditwheel-emscripten
# pyodide-build
+15 -10
View File
@@ -33,6 +33,7 @@ class EnableGroup(StrEnum):
GraalPy = "graalpy"
PyPy = "pypy"
PyPyEoL = "pypy-eol"
PyodideEoL = "pyodide-eol"
PyodidePrerelease = "pyodide-prerelease"
@classmethod
@@ -93,17 +94,21 @@ class BuildSelector:
return False
if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
return False
# TODO: Re-enable this when we have Pyodide prereleases again (e.g., 0.29.0a1+)
# Python 3.13 support became stable in Pyodide 0.28.0, so it no longer needs a prerelease
# flag.
# Also update Pyodide tests in unit_test/build_selector_test.py accordingly.
# When re-enabling, update the pattern to match the experimental Python version in case
# it is bumped to Python 3.14 (likely cp314-pyodide_* but could remain as 3.13 as well).
if EnableGroup.PyodideEoL not in self.enable and fnmatch(build_id, "cp312-pyodide_*"):
return False
# NOTE: Disable this when we don't have any Pyodide prereleases (e.g., 314.0.0a1+)
# When doing this, also:
# 1. update Pyodide tests in unit_test/build_selector_test.py and unit_test/options_test.py accordingly.
# 2. update Python versions for Pyodide identifiers in cibuildwheel/selector.py.
# 3. update constraints as necessary via bin/generate_pyodide_constraints.py and add/delete
# Pyodide constraints files in cibuildwheel/resources/constraints/ as necessary.
# When disabling, update the pattern to match the experimental Python version in case
# it is bumped to Python 3.15 (likely cp315-pyodide_* but could remain as 3.14 as well).
# This depends on the CPython version being used in the Pyodide runtime at the time.
# if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
# build_id, "cp313-pyodide_*"
# ):
# return False
if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
build_id, "cp314-pyodide_*"
):
return False
should_build = selector_matches(self.build_config, build_id)
should_skip = selector_matches(self.skip_config, build_id)
+113
View File
@@ -0,0 +1,113 @@
# Maintaining Pyodide support
Last updated: May 2026
This page describes how to update cibuildwheel's Pyodide platform code when either:
- a new Pyodide alpha release arrives with support for a new [PyEmscripten Platform](https://pyodide.org/en/latest/development/abi.html) (which is tied to updates in Emscripten and CPython versions, compiler/linker flags, and so on), or
- when that alpha release graduates to a stable one.
## Background
Pyodide has three types of releases that matter to cibuildwheel:
- **Stable** the most recent full Pyodide release (e.g., `0.29.x` / cp313). This is enabled by default with no special `CIBW_ENABLE` flag needed.
- **Prerelease** an alpha/beta/rc Pyodide release that uses the _next_ CPython version (e.g., `314.0.0a1` / cp314). Users must opt in with `CIBW_ENABLE: pyodide-prerelease` to build against this version. This may or may not be available at any given time, depending on the Pyodide release cycle.
- **End-of-life (EoL)** older Pyodide stable releases that are no longer the current stable. These are kept available behind `CIBW_ENABLE: pyodide-eol` so that users who still need to build for older Pyodide versions can do so.
The guards in `cibuildwheel/selector.py` enforce this distinction. The constraints files under `cibuildwheel/resources/` pin the exact tool versions that go with each build.
---
## When a new Pyodide prerelease becomes available
For example, consider a scenario when Pyodide ships a new `315.0.0a1` with cp315 support.
### 1. Add the new Python configuration
In `cibuildwheel/resources/build-platforms.toml`, add an entry under `[pyodide]`:
```toml
{ identifier = "cp315-pyodide_wasm32", version = "3.15", default_pyodide_version = "315.0.0a1", node_version = "v24" },
```
`version` is the CPython version string, `default_pyodide_version` is the Pyodide release to use when the user does not pin one explicitly (use the latest available alpha/beta for a prerelease entry), and `node_version` is the minimum Node.js major required by that Pyodide release — check the [pyodide-build FAQ](https://pyodide-build.readthedocs.io/en/latest/faq.html#what-node-js-version-do-i-need) for a rudimentary idea of what the correct value is.
### 2. Update the prerelease guards in the selector
In `cibuildwheel/selector.py`, update the patterns in the `PyodidePrerelease` guards to match the new identifier:
```python
if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
build_id, "cp315-pyodide_*"
):
return False
```
### 3. Generate and pin a constraints file
Run the `update_constraints` `nox` session, which reads `build-platforms.toml` and regenerates all Pyodide constraints files automatically:
```bash
nox -s update_constraints
```
### 4. Update tests
Update the unit tests so the new identifier is accepted by the selector with `PyodidePrerelease` enabled and rejected without it. Pyodide-specific integration tests may also need their hardcoded expected-wheel lists extended.
## When a Pyodide prerelease becomes stable
Pyodide uses a versioning scheme where the stable release for a given CPython version is named `[PythonMajorMinor].0.0`, so the first stable release shipping cp314 will be **`314.0.0`**. See [pyodide/pyodide#6084](https://github.com/pyodide/pyodide/issues/6084) for a rationale of this versioning scheme.
### 1. Update the stable entries, and remove (or replace) the prerelease entry
In `build-platforms.toml`, update the former prerelease entry's `default_pyodide_version` to the new stable release (e.g. `314.0.0`) and remove the prerelease marker from the identifier if present. Remove previous prerelease entries if they are now obsolete, or update them to the next prerelease if one is available. Move the previous stable version behind `pyodide-eol` rather than dropping it outright (see below).
### 2. Disable or update the prerelease guards
In `selector.py`:
- **If a new prerelease is available**: update the `fnmatch` pattern to the next identifier (e.g. `cp315-pyodide_*`) as described above.
- **If there is no new prerelease**: comment out the `PyodidePrerelease` logic.
### 3. Update the constraints file
Run `nox -s update_constraints` to regenerate the constraints file for the newly stable version. If the entry was already in `build-platforms.toml` as a prerelease, its constraints file already exists, and this step just refreshes it against the stable Pyodide release and updates the dependencies' versions.
### 4. Update tests
Update the unit tests so the newly stable identifier is accepted by the selector without needing `PyodidePrerelease` in the enable set. Pyodide-specific integration tests may also need their hardcoded expected-wheel lists extended.
## When an old Pyodide version is to be moved to end-of-life
When a Pyodide version is superseded by a new stable release, move it behind the `pyodide-eol` enable flag. We want to allow users who still build for older Pyodide ABIs time to upgrade.
### 1. Add the `pyodide-eol` guard in the selector
In `cibuildwheel/selector.py`, add (or update) the `PyodideEoL` guard to include the old identifier:
```python
if EnableGroup.PyodideEoL not in self.enable and fnmatch(build_id, "cp312-pyodide_*"):
return False
```
### 2. Update tests
Update the unit tests so the EoL identifier requires `PyodideEoL` to be included in the enable set. The default (no `CIBW_ENABLE`) should exclude it.
## When an old Pyodide version is to be fully retired
Retirement is not expected to happen on a routine basis. It is only warranted when the Pyodide ecosystem itself has evolved to the point where an older ABI version is considered obsolete for example, if the surrounding toolchain, packaging standards, or runtime infrastructure have moved on so substantially that building for the older version no longer makes practical sense. Any retirement is to be discussed and agreed upon by Pyodide maintainers before proceeding.
### 1. Remove the Python configuration
Delete the entry from `build-platforms.toml` and remove the `PyodideEoL` guard for that identifier in `selector.py`.
### 2. Delete the constraints file
Remove `cibuildwheel/resources/constraints-pyodideXYZ.txt`.
### 3. Update tests
Remove references to the old identifier from the unit tests, integration tests, and drop any expected-wheel entries for it from the test helper.
+6
View File
@@ -208,3 +208,9 @@ git push && git push --tags
Then head to https://github.com/pypa/cibuildwheel/releases and create a GitHub release from the new tag, pasting in the changelog entry. Once the release is created inside GitHub, a CI job will create the assets and upload them to PyPI.
If there were any schema updates, run `pipx run ./bin/generate_schema.py --schemastore > partial-cibuildwheel.json` and contribute the changes to SchemaStore.
### Platform-specific maintenance
This section is a stub. Please open a PR to add guidance for any platform you would like to help maintain!
- **Pyodide**: see [Maintaining Pyodide support](_internal/pyodide-maintenance.md) for instructions and maintainer-specific information on updating Pyodide-related code in cibuildwheel and updating to new Pyodide releases.
+2
View File
@@ -361,6 +361,8 @@ values are:
- `pyodide-prerelease`: Pyodide versions that haven't released yet, if one is
available. Safe if you are shipping a site with an early build, not for
general distribution.
- `pyodide-eol`: Enable Pyodide versions that are no longer the current stable
release (if still available).
- `all`: Enable all of the above.
!!! caution
+1 -3
View File
@@ -159,8 +159,6 @@ By default, `ARM64` is not enabled when running on non-`ARM64` runners. Use [`CI
## Pyodide/WebAssembly {: #pyodide}
Pyodide is offered as an experimental feature in cibuildwheel.
### System requirements
Pyodide builds require a Linux or macOS machine.
@@ -173,7 +171,7 @@ You must target pyodide with `--platform pyodide` (or use `--only` on the identi
It is also possible to target a specific Pyodide version by setting the [`pyodide-version`](options.md#pyodide-version) option to the desired version. Users are responsible for setting an appropriate Pyodide version according to the `pyodide-build` version. A list is available in Pyodide's [cross-build environments metadata file](https://github.com/pyodide/pyodide/blob/main/pyodide-cross-build-environments.json), which can be viewed more easily by installing `pyodide-build` from PyPI and using `pyodide xbuildenv search --all` to see a compatibility table.
If there are pre-releases available for a newer Pyodide version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions.
If there are pre-releases available for a newer Pyodide version, the `pyodide-prerelease` [`enable`](options.md#enable) can be used to include pre-release versions. To build for older Pyodide versions that are no longer the current stable, use the `pyodide-eol` [`enable`](options.md#enable).
### Running tests
+7 -2
View File
@@ -43,15 +43,20 @@ def test_abi3(tmp_path: Path) -> None:
project_dir,
add_env={
# free_threaded, GraalPy, and PyPy do not have a Py_LIMITED_API equivalent, just build one of those
# pyodide uses cp313 (the stable version) which supports limited API / abi3
# also limit the number of builds for test performance reasons
"CIBW_BUILD": "cp39-* cp310-* pp310-* gp312_250-* cp312-* cp314t-*",
"CIBW_BUILD": (
"cp313-*"
if utils.get_platform() == "pyodide"
else "cp39-* cp310-* pp310-* gp312_250-* cp312-* cp314t-*"
),
"CIBW_ENABLE": "all",
},
)
# check that the expected wheels are produced
if utils.get_platform() == "pyodide":
# there's only 1 possible configuration for pyodide, cp312. It builds
# there's only 1 possible configuration for pyodide, cp313. It builds
# a wheel that is tagged abi3, compatible back to 3.10
expected_wheels = utils.expected_wheels(
"spam",
+3 -3
View File
@@ -18,10 +18,10 @@ from pathlib import Path
wheel = Path(sys.argv[1])
dest_dir = Path(sys.argv[2])
platform = wheel.stem.split("-")[-1]
if platform.startswith("pyodide"):
# for the sake of this test, munge the pyodide platforms into one, it's
if platform.startswith("pyemscripten"):
# for the sake of this test, munge the pyemscripten platforms into one, it's
# not valid, but it does activate the uniqueness check
platform = "pyodide"
platform = "pyemscripten"
name = f"spam-0.1.0-py2-none-{platform}.whl"
dest = dest_dir / name
+17 -8
View File
@@ -77,13 +77,13 @@ def get_versions_from_constraint_file(constraint_file: Path) -> dict[str, str]:
return dict(re.findall(VERSION_REGEX, constraint_file_text))
@pytest.mark.parametrize("python_version", ["3.8", "3.12"])
@pytest.mark.parametrize("python_version", ["3.8", "3.13"])
def test_pinned_versions(
tmp_path: Path, python_version: str, build_frontend_env_nouv: dict[str, str]
) -> None:
if utils.get_platform() == "linux":
pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead")
if python_version != "3.12" and utils.get_platform() == "pyodide":
if python_version != "3.13" and utils.get_platform() == "pyodide":
pytest.skip(f"pyodide does not support Python {python_version}")
if (
python_version == "3.8"
@@ -124,7 +124,7 @@ def test_pinned_versions(
expected_wheels = [
w
for w in utils.expected_wheels("spam", "0.1.0")
if f"-cp{version_no_dot}" in w or f"-pp{version_no_dot}" in w
if f"-cp{version_no_dot}-cp{version_no_dot}-" in w or f"-pp{version_no_dot}-" in w
]
assert set(actual_wheels) == set(expected_wheels)
@@ -140,11 +140,20 @@ def test_dependency_constraints(
project_dir = tmp_path / "project"
test_projects.new_c_project().generate(project_dir)
tool_versions = {
"pip": "23.1.2",
"build": "1.2.2",
"delocate": "0.10.3",
}
if utils.get_platform() == "pyodide":
# pyodide-build 0.34+ requires build~=1.4.0, so we must use a
# compatible version here. delocate is macOS-only and not used on pyodide.
tool_versions = {
"pip": "23.1.2",
"build": "1.4.2",
"delocate": "0.10.3",
}
else:
tool_versions = {
"pip": "23.1.2",
"build": "1.2.2",
"delocate": "0.10.3",
}
if method == "file":
constraints_file = tmp_path / "constraints file.txt"
+3 -3
View File
@@ -34,7 +34,7 @@ def test_cross_compiled_build(tmp_path: Path) -> None:
add_env={"CIBW_ARCHS": "x86_64, universal2, arm64"},
single_python=True,
)
python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
python_tag = "cp{0}{1}-cp{0}{1}-".format(*utils.SINGLE_PYTHON_VERSION)
expected_wheels = [w for w in ALL_MACOS_WHEELS if python_tag in w]
assert set(actual_wheels) == set(expected_wheels)
@@ -176,7 +176,7 @@ def test_universal2_testing_on_x86_64(
else:
assert warning_message in captured.err
python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
python_tag = "cp{0}{1}-cp{0}{1}-".format(*utils.SINGLE_PYTHON_VERSION)
expected_wheels = [w for w in ALL_MACOS_WHEELS if python_tag in w and "universal2" in w]
assert set(actual_wheels) == set(expected_wheels)
@@ -210,7 +210,7 @@ def test_universal2_testing_on_arm64(
assert "running tests on arm64 with pillow" in captured.out
assert "running tests on x86_64 with pillow" in captured.out
python_tag = "cp{}{}".format(*utils.SINGLE_PYTHON_VERSION)
python_tag = "cp{0}{1}-cp{0}{1}-".format(*utils.SINGLE_PYTHON_VERSION)
expected_wheels = [w for w in ALL_MACOS_WHEELS if python_tag in w and "universal2" in w]
assert set(actual_wheels) == set(expected_wheels)
+5 -5
View File
@@ -79,8 +79,8 @@ def test_pyodide_build(tmp_path: Path, use_pyproject_toml: bool) -> None:
# check that the expected wheels are produced
expected_wheels = [
"spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
"spam-0.1.0-cp313-cp313-pyodide_2025_0_wasm32.whl",
"spam-0.1.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl",
"spam-0.1.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
]
print("actual_wheels", actual_wheels)
@@ -144,8 +144,8 @@ def test_pyodide_build_and_test(tmp_path: Path, expect_failure: bool) -> None:
)
# check that the expected wheels are produced
expected_wheels = [
"spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
"spam-0.1.0-cp313-cp313-pyodide_2025_0_wasm32.whl",
"spam-0.1.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl",
"spam-0.1.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
]
assert set(actual_wheels) == set(expected_wheels)
@@ -170,6 +170,6 @@ def test_pyodide_repair_wheel(tmp_path: Path) -> None:
# check that the expected wheels are produced
expected_wheels = [
"spam-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl",
"spam-0.1.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl",
]
assert set(actual_wheels) == set(expected_wheels)
+10 -9
View File
@@ -26,7 +26,7 @@ EMULATED_ARCHS: Final[list[str]] = sorted(
PYPY_ARCHS = ["x86_64", "i686", "AMD64", "aarch64", "arm64"]
GRAALPY_ARCHS = ["x86_64", "AMD64", "aarch64", "arm64"]
SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 12)
SINGLE_PYTHON_VERSION: Final[tuple[int, int]] = (3, 13)
# temporary workaround: set by build_frontend_env fixture to skip graalpy
# when uv is the build frontend (compatibility issue between graalpy and uv)
@@ -263,11 +263,11 @@ def _expected_wheels(
if musllinux_versions is None:
musllinux_versions = ["musllinux_1_2"]
# To be kept in sync with Python versions for Pyodide identifiers in cibuildwheel/selector.py.
if platform == "pyodide" and python_abi_tags is None:
python_abi_tags = [
"cp312-cp312",
"cp313-cp313",
]
python_abi_tags = ["cp313-cp313"]
if EnableGroup.PyodidePrerelease in enable_groups:
python_abi_tags.append("cp314-cp314")
elif platform == "android" and python_abi_tags is None: # noqa: SIM114
python_abi_tags = [
"cp313-cp313",
@@ -412,13 +412,14 @@ def _expected_wheels(
elif platform == "pyodide":
platform_tags = {
"cp312-cp312": ["pyodide_2024_0_wasm32"],
"cp313-cp313": ["pyodide_2025_0_wasm32"],
"cp313-cp313": ["pyemscripten_2025_0_wasm32"],
"cp314-cp314": ["pyemscripten_2026_0_wasm32"],
}.get(python_abi_tag, [])
if not platform_tags:
# for example if the python tag is `none` or `abi3`, all
# platform tags are built with that python tag
platform_tags = ["pyodide_2024_0_wasm32"]
# for example if the python tag is `none` or `abi3`, the wheel
# is built by the stable cp313 Python version
platform_tags = ["pyemscripten_2025_0_wasm32"]
else:
msg = f"Unsupported platform {platform!r}"
+13 -1
View File
@@ -97,8 +97,8 @@ def test_build_filter_pyodide_prerelease() -> None:
skip_config="",
enable=frozenset([EnableGroup.PyodidePrerelease]),
)
assert build_selector("cp312-pyodide_wasm32")
assert build_selector("cp313-pyodide_wasm32")
assert build_selector("cp314-pyodide_wasm32")
def test_build_filter_pyodide() -> None:
@@ -107,8 +107,20 @@ def test_build_filter_pyodide() -> None:
skip_config="",
enable=frozenset(),
)
assert build_selector("cp313-pyodide_wasm32")
assert not build_selector("cp312-pyodide_wasm32")
assert not build_selector("cp314-pyodide_wasm32")
def test_build_filter_pyodide_eol() -> None:
build_selector = BuildSelector(
build_config="*",
skip_config="",
enable=frozenset([EnableGroup.PyodideEoL]),
)
assert build_selector("cp312-pyodide_wasm32")
assert build_selector("cp313-pyodide_wasm32")
assert not build_selector("cp314-pyodide_wasm32")
def test_skip() -> None:
+1 -1
View File
@@ -308,7 +308,7 @@ def test_pyodide_on_windows(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
monkeypatch.setattr(sys, "platform", "win32")
monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", "cp312-pyodide_wasm32"])
monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", "cp314-pyodide_wasm32"])
with pytest.raises(SystemExit) as exit:
main()
+2 -2
View File
@@ -35,7 +35,7 @@ manylinux-x86_64-image = "manylinux_2_28"
environment-pass = ["EXAMPLE_ENV"]
pyodide-version = "0.28.0"
pyodide-version = "0.29.4"
[tool.cibuildwheel.macos]
test-requires = "else"
@@ -92,7 +92,7 @@ def test_options_1(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_34"]
local = options.build_options("cp312-pyodide_wasm32")
assert local.pyodide_version == "0.28.0"
assert local.pyodide_version == "0.29.4"
def test_passthrough(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: