Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
87bcefe
Use `pyodide config get emscripten_dir`
agriyakhetarpal Apr 6, 2026
17679b8
Update constraints
agriyakhetarpal Apr 6, 2026
2df97d1
Updates for `pyemscripten` platform tag
agriyakhetarpal Apr 6, 2026
4bd785d
Weird constraints stuff...
agriyakhetarpal Apr 6, 2026
499e965
Revert "Weird constraints stuff..."
agriyakhetarpal Apr 6, 2026
8064a46
Add some special constraints for pyodide
agriyakhetarpal Apr 6, 2026
ac56575
Wheel pyemscripten collision
agriyakhetarpal Apr 6, 2026
52fa455
Put delocate back in constraints (not needed tho)
agriyakhetarpal Apr 6, 2026
46a2627
Drop Pyodide 0.27.7 (cp312-pyodide_wasm32)
agriyakhetarpal Apr 10, 2026
088d6e5
Add Pyodide 314.0.0a1 (cp314-pyodide_wasm32)
agriyakhetarpal Apr 10, 2026
455e568
Delete no-longer-needed Pyodide 312 constraints
agriyakhetarpal Apr 10, 2026
9396cae
Update Pyodide 313 and 314 constraints
agriyakhetarpal Apr 10, 2026
f0289d8
Add back prerelease stuff and update tests
agriyakhetarpal Apr 10, 2026
9749c11
Fix `test_pyodide_on_windows`
agriyakhetarpal Apr 10, 2026
e3c8ca1
Update and fix tests some more
agriyakhetarpal Apr 10, 2026
f4e5229
Also enable pyodide-prerelease in GHA sample build
agriyakhetarpal Apr 10, 2026
3fe3394
Update tests yet again (`SINGLE_PYTHON_VERSION` changes)
agriyakhetarpal Apr 10, 2026
6092434
Add a Pyodide-specific maintenance guide
agriyakhetarpal Apr 10, 2026
87c1be7
Merge branch 'main' into pyodide-updates
agriyakhetarpal Apr 10, 2026
40ffec0
Add suggestions from Hood and Gyeongjae
agriyakhetarpal Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,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 }}
Expand Down
21 changes: 10 additions & 11 deletions cibuildwheel/platforms/pyodide.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,29 +94,28 @@ 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",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need force?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need it to ensure that we always have a fresh copy of the Emscripten toolchain installed and patched. One might argue that cibuildwheel runs in CI on fresh runners and therefore this wouldn't be necessary, but I would suggest there are two reasons:

  1. Sequential builds: if, for whatever reason, someone builds multiple Pyodide wheels in CI using separate cibuildwheel CLI invocations or GitHub Actions within the same job in a workflow, then we will always have a self-contained build that does not rely on a stale Emscripten toolchain.
  2. Security: although this may be somewhat unlikely, cibuildwheel does execute user code, which could potentially tamper with the Emscripten toolchain with the right payload when building the wheel. I am aware that this risk could apply to any file on a system too, but even if we confine ourselves to this scenario, it would be beneficial for the user to have confidence that their Emscripten installation is always the one we intended them to use. This also supports the use case for cibuildwheel outside of CI and ties into the staleness aspect in point 1. I tend to use cibuildwheel to build Pyodide wheels locally quite often.

"--version",
version,
"--path",
str(xbuildenv_cache_path),
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]:
Expand Down Expand Up @@ -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"]])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should put this path before or after the original PATH.

In pyodide-build, if a system emscripten is installed, it dominates. This is to ensure we don't shadow the user-installed emscripten. However, regarding the cibuildwheel would normally run in CI (I guess)? It is rare that people would want to install emscripten themselves, so this might make sense.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. We have been doing env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]]) even before #2800, i.e., in all our previous releases with Pyodide support. So, yes, I would expect a user of pyodide-build standalone to want a higher level of control over the build process and be able to their own Emscripten installation, whereas a user of cibuildwheel would trust what it does and not mess with the moving parts required to build Pyodide wheels. This means cibuildwheel should dominate with its Emscripten installation indeed, like it does right now.

That said, we can always change this behaviour and append to PATH if someone requests it at a later point in time, of course.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for this one, I'm happy for cibuildwheel to control the environment. PATH can have all sorts of things on it that cause users issues, actually.

I also wonder if PYODIDE_EMSCRIPTEN_DIR would be a way for advanced users to work around this?

Seems like it'd do what we want-

$ PYODIDE_EMSCRIPTEN_DIR=/tmp/somedir pyodide config get emscripten_dir
/tmp/somedir


return env

Expand Down
2 changes: 1 addition & 1 deletion cibuildwheel/resources/build-platforms.toml
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ python_configurations = [

[pyodide]
python_configurations = [
{ identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.7", node_version = "v22" },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to remove 3.12 here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, since I don't think it's useful to have three ABIs enabled at once. We don't support anything but the latest version. Pyodide 0.29.3 and 314.0.0a1 have two different ABIs, which should be enough? Since we don't have a stable 314.0.0 release and IMO cibuildwheel shouldn't be as disruptive to users' workflows, I think it's okay to keep 0.29.3 – but three different ABIs becomes a bit excessive. People can still build for arbitrary (read: older) versions, such as Pyodide 0.27.7, using CIBW_PYODIDE_VERSION. Or would you prefer I keep three ABIs?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the usage of the old versions? IMO, people can upgrade, and probably should, but it's also easy to forget. I'm sure I have webpages that are using old versions.

I think the pyemscripten tag is much more important than the one you can't upload to PyPI, so I think non-pyemscripten are really low priority, I don't mind dropping any of them.

Copy link
Copy Markdown
Contributor

@hoodmane hoodmane Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per jsdelivr, the most-used version is Python 3.12, followed by 3.13 and 3.10 which are almost tied.

Python 3.13: 2,033,897
Python 3.12: 6,602,877
Python 3.11:   697,276
Python 3.10: 1,929,739

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but three different ABIs becomes a bit excessive

What's the excess you're considering? The requirement for the user to grab 3 different emscripten toolchains?

I think that supporting 3 current + one prerelease version seems quite reasonable to me going forward. But perhaps we'd like to hide one or two behind a pyodide-eol enable flag to keep them off by default.

This current scenario might be an exception, though, as the package consumer is gonna need to upgrade something anyway, so I can see the argument to drop more aggressively here.

In summary, my (weakly held) opinion would be to drop 3.12 now, but going forward, I'm open to supporting more versions than the current behind a pyodide-eol flag.

{ identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.29.3", node_version = "v22" },
{ identifier = "cp314-pyodide_wasm32", version = "3.14", default_pyodide_version = "314.0.0a1", node_version = "v24" },
]

[android]
Expand Down
18 changes: 9 additions & 9 deletions cibuildwheel/resources/constraints-pyodide313.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.4.2
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.2.25
# via requests
charset-normalizer==3.4.6
charset-normalizer==3.4.7
# via requests
click==8.1.8
# via
Expand Down Expand Up @@ -39,7 +39,7 @@ packaging==26.0
# wheel
pip==26.0.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.4
platformdirs==4.9.6
# via
# pyodide-build
# python-discovery
Expand All @@ -50,9 +50,9 @@ pydantic==2.12.5
# pyodide-lock
pydantic-core==2.41.5
# via pydantic
pygments==2.19.2
pygments==2.20.0
# via rich
pyodide-build==0.33.0
pyodide-build==0.34.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
Expand All @@ -62,9 +62,9 @@ pyodide-lock==0.1.2
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
python-discovery==1.2.2
# via virtualenv
requests==2.32.5
requests==2.33.1
# via pyodide-build
rich==14.3.3
# via
Expand All @@ -81,7 +81,7 @@ typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.2.0
virtualenv==21.2.1
# via
# build
# pyodide-build
Expand Down
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Git/GitHub shows this file as renamed and can't identify this – more accurately, the 3.12 constraints were deleted and followed by the creation of a new file for the 3.14 constraints.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can split them into two commits, and then we can rebase and merge instead of squash.

Original file line number Diff line number Diff line change
Expand Up @@ -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.4.2
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.2.25
# via requests
charset-normalizer==3.4.6
charset-normalizer==3.4.7
# via requests
click==8.1.8
# via
Expand Down Expand Up @@ -39,7 +39,7 @@ packaging==26.0
# wheel
pip==26.0.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.4
platformdirs==4.9.6
# via
# pyodide-build
# python-discovery
Expand All @@ -50,9 +50,9 @@ pydantic==2.12.5
# pyodide-lock
pydantic-core==2.41.5
# via pydantic
pygments==2.19.2
pygments==2.20.0
# via rich
pyodide-build==0.33.0
pyodide-build==0.34.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
Expand All @@ -62,9 +62,9 @@ pyodide-lock==0.1.2
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
python-discovery==1.2.2
# via virtualenv
requests==2.32.5
requests==2.33.1
# via pyodide-build
rich==14.3.3
# via
Expand All @@ -81,7 +81,7 @@ typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.2.0
virtualenv==21.2.1
# via
# build
# pyodide-build
Expand Down
22 changes: 12 additions & 10 deletions cibuildwheel/selector.py
Comment thread
agriyakhetarpal marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,19 @@ def __call__(self, build_id: str) -> bool:
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).
# 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_*"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is slightly uncomfortable that we need to turn this on and off every time we make an alpha release. Would there be a way to remove this?

As we standardized pyemscripten platform now, I think there is no reason to differentiate Pyodide prerelease and stable. Both versions should satisfy the pyemscripten platform constraints and users doesn't have to know whether they are using alpha xbuildenv or not.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need a notion of alpha versus stable I'd say. We can change the platform after alpha releases so people should wait for a stable release to upload wheels.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change the platform after alpha releases

Hmm, my idea was to freeze the ABI as of alpha release, and that is what I was going to say in the blog post (pyodide/pyodide-blog#66).

I think we all agreed freezing the ABI earlier before making a stable release (pyodide/pyodide#5580), do you think we need another, say "beta" release for that then?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I agree with @hoodmane here. I think we should still make the difference between alpha and stable. This is because even for PyPI and wheels for conventional (desktop) platforms, it is advised to only test building wheels with an upcoming ABI – not upload them. Uploading to PyPI does not happen until there is a stable CPython release.

Doing this also protects us in the event where may need to change the ABI after our proclamation of stabilising it, due to any bugs or regressions that get caught. We only have to do this process a few times a year, at most, so I would personally be fine with it flipping the switches on and off in this manner.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I thought PyPI allows uploading Python wheels when CPython alpha or beta release is out. If it doesn't, then it would make sense for us to do the same approach as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think CPython does so starting with b1, but it is still discouraged, IIRC. @henryiii, I assume you'd be the right person to know about this topic!

Copy link
Copy Markdown
Member Author

@agriyakhetarpal agriyakhetarpal Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even then, freezing the ABI and making the announcement is still fine I think – the more eyes we receive on this, the better our adoption will be for PEP 783, as downstream package authors will be better prepared to make the jump.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython is not guaranteed to be ABI stable till the last beta release (historically, everyone basically uses RC1 as the first "stable" release, and that's what cibuildwheel does, but officially it's last beta, beta 4, at least in the past).

So you should not upload a wheel build with an early beta, but I don't think anyone stops it. In cibuildwheel you acknowledge it being a bad idea to upload with the enable opt-in.

IMO this should be removed whenever the ABI is stable. Doesn't have to match some special "final" release, but it has to be ensured to be stable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is required as long as the default_pyodide_version in build-platforms is set to a prerelease version.

Once the first 'proper' pyodide release is out for 3.14, we'll announce it in the release notes, remove this line, our users will begin building for it, we'll never set that default_pyodide_version to a prerelease version again. So this only toggles on/off once per CPython minor release.

Then again, if the ABI will be frozen as of the first alpha, you could argue we don't need the pyodide-prerelease flag at all. I'd find that a little surprising - cibuildwheel's users would be getting alpha-quality software without specifically asking for it - but I'm not across the topic enough to know if that's a good idea or not.

):
return False

should_build = selector_matches(self.build_config, build_id)
should_skip = selector_matches(self.skip_config, build_id)
Expand Down
109 changes: 109 additions & 0 deletions docs/_internal/pyodide-maintenance.md
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice as a point-in-time capture of the current thinking! I'll just caution that these kinds of maintainer notes often go out-of-date or else require lots of maintenance as time goes on. Two ideas-

  • It might be a good idea to chuck a "Last updated: April 2026" note in the file so we know how fresh it is.
  • I'd recommend keeping it as short as possible, so we have less to maintain down the road!

But, if you're happy to keep it updated @agriyakhetarpal, and it's helpful for you, this sounds good to me.

Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Maintaining Pyodide support

This page describes some short steps on how to update cibuildwheel's Pyodide platform code when the set of supported Pyodide Python versions changes – for example, when:
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

- a new Pyodide alpha release arrives with support for a new [Pyodide ABI](https://pyodide.org/en/latest/development/abi.html) (which may be tied to updates in Emscripten and CPython versions, compiler/linker flags, Rust toolchain support and so on), or
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated
- when that alpha graduates to a stable release.

## Background

Pyodide ships/may ship two kinds of Python builds at any point in time (note that these numbers may not be up to date):
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

- **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.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in the previous comment, I think we should remove differentiating Pyodide alpha from stable at least in cibuildwheel-side of view.


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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The node_version situation is a bit wishy-washy. We should improve our docs on node versions. No action needed here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed


### 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
```

Also, update the comment above it to reflect the new version numbers and the instructions for when this guard should eventually be disabled.
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

### 3. Generate and pin a constraints file

The easiest way is to run the `update_constraints` `nox` session, which reads `build-platforms.toml` and regenerates all Pyodide constraints files automatically:
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

```bash
nox -s update_constraints
```

Alternatively, run the generator script directly:

```bash
python bin/generate_pyodide_constraints.py 315.0.0a1 \
| uv pip compile - --python-version=3.15 \
-o cibuildwheel/resources/constraints-pyodide315.txt
```

### 4. Update tests and update CI configuration

- 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.

- Run the full test suite with `CIBW_PLATFORM=pyodide` and `CIBW_ENABLE=pyodide-prerelease` environment variables to make sure the new configuration is well exercised in CI.

- In GHA, make sure `CIBW_ENABLE: pyodide-prerelease` is set in the Pyodide CI job so that the prerelease identifier is exercised in CI.
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

## 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. Based on your discretion, you may choose to keep the old stable entry for a while if it is still supported by Pyodide, or remove it immediately if it is already retired.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify this to say just whatever the most likely thing is? We're talking to ourselves here, if we later disagree with the advice we can deviate from it or change it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to decide on this before I change this sentence, because we want to be aligned on a policy for how many Pyodide versions/ABIs we should support at a time in cibuildwheel. So far, it's always been two at a time (or even one, some time back). Please read my other comment: #2812 (comment)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bin/update_pythons.py defines a lot of the logic, especially around default_pyodide_version. It might be cleaner when adding a version, to add a stub entry to build-platforms.toml and then run the script. If we need some behaviour that's not captured by the script, we'll have to update it, otherwise the Update Dependencies PRs will fight it.


### 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**L remove the `PyodidePrerelease` logic entirely.
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

### 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 and CI configuration

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.

- Run the full test suite with `CIBW_PLATFORM=pyodide` environment variables to make sure the new configuration is well exercised in CI.

- Remove `CIBW_ENABLE: pyodide-prerelease` from the GHA Pyodide CI job steps, as no prerelease identifiers should remain after the stable release ships.
Comment thread
agriyakhetarpal marked this conversation as resolved.
Outdated

## When an old Pyodide version is to be retired

### 1. Remove the python configuration

Delete the entry from `build-platforms.toml`.

### 2. Delete the constraints file

Remove `cibuildwheel/resources/constraints-pyodideXYZ.txt`.

### 3. Update tests and CI configuration

Remove references to the old identifier from the unit tests, integration tests, and drop any expected-wheel entries for it from the test helper. Also, ensure that no CI job is still trying to build it.
6 changes: 6 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
9 changes: 7 additions & 2 deletions test/test_abi_variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions test/test_custom_repair_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
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
Expand Down
Loading
Loading