Compare commits

...
87 Commits
Author SHA1 Message Date
Joe Rickerby e5486e0b83 Bump version: v2.23.4 2026-03-16 19:31:17 +00:00
ff9508a3e1 [2.x] Update virtualenv URLs to avoid blob URLs (#2775)
* [2.x] Update virtualenv URLs to avoid blob URLs

It looks like many people are hitting the HTTP 429 for old versions of cibuildwheel. These hosted versions of virtualenv should work, let's see...

* Write the correct version strings

* ci: some updates based on main branch

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: move to using latest images

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore: fix up style checks

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: newer azure images too

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: use new SSL test from main

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Drop `build`'s installation from Pyodide build tools

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>
2026-03-16 19:24:46 +00:00
Joe Rickerby faf86a6ed7 Bump version: v2.23.3 2025-04-26 11:39:25 +01:00
Joe Rickerby 4241f37b2c [2.x] Update dependencies (#2371) 2025-04-26 11:35:02 +01:00
Joe Rickerby d04cacbc98 Bump version: v2.23.2 2025-03-24 21:25:38 +00:00
Matthieu Darbois 5f4e019684 [2.x] Update dependencies (#2334) 2025-03-24 21:17:56 +00:00
Henry Schreiner 2efa648f38 fix: always resolve --python argument (#2328) (#2331)
fix: always resolve --python argument (#2328)

fix: always resolve the --python argument

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-03-23 15:03:30 -04:00
Joe Rickerby 42728e866b Bump version: v2.23.1 2025-03-15 17:58:14 +00:00
Joe Rickerby 6e1527b153 Fix unit test when other warnings are present 2025-03-15 17:54:37 +00:00
Matthieu Darbois c25fe60385 fix: image deprecation warning (#2314) 2025-03-15 17:54:37 +00:00
Henry SchreinerandJoe Rickerby a880bf5105 fix: warn on deprecated images being set (#2312)
* fix: warn on deprecated images being set

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: handle repeated warning messages

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Joe Rickerby <joerick@mac.com>

* fix: try making the warnings once per Options

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-03-15 17:54:37 +00:00
Joe Rickerby c087d85b69 Update dependencies 2025-03-15 17:54:37 +00:00
Joe Rickerby 6cccd09a31 Bump version: v2.23.0 2025-03-01 13:27:50 +00:00
Matthieu Darbois aa1534d2a3 chore(2.x): update dependencies (#2294) 2025-03-01 11:49:19 +00:00
mayeut ee23dd147b fix(test): implement retry for test_container_removed
The test is flaky on some platforms. Implement retry rather than just skip.
2025-02-25 14:22:19 -05:00
Matthieu Darbois 55037e9716 ci/doc: move azure macOS build to macOS-13 (#2101)
The macOS-12 environment is deprecated.
2025-02-25 14:22:19 -05:00
Joe Rickerby 8874ee2048 Remove specific Python versions from the update-dependencies job
The requirements pinning is done by uv now, so we don't need to
run the versions of Python to do the pinning anymore.

Cherry-picked from 37f7f611b208f48d4bb80a35eb31e39e47102930
2025-02-25 14:22:19 -05:00
mayeut f76d345e81 chore: bump dependencies 2025-02-25 14:22:19 -05:00
Agriya Khetarpal 5a5e514fbd Bump to Pyodide 0.27 (#2117)
* Bump to Pyodide 0.27

* Update the Pyodide target's constraints
2025-02-25 14:22:19 -05:00
Matthieu Darbois 017abbba68 feature: add PyPy 3.11 (#2268) 2025-02-25 14:22:19 -05:00
Matthieu Darbois bb1fd3320e feat: Add support for ubuntu-24.04-arm GHA runner (#2135)
* ci(gha): add tests on ubuntu-24.04-arm

* address review comments
2025-02-25 14:22:19 -05:00
Matthieu Darbois 98a6f3bf64 chore: use pypa/manylinux for manylinux_2_31_armv7l (#2269) 2025-02-25 14:22:19 -05:00
Henry Schreiner ee63bf16da Bump version: v2.22.0 2024-11-23 01:44:09 -05:00
Alex Waygood d3eeba7967 chore: bump Ruff to 0.8.0 (#2092) 2024-11-22 16:18:12 -05:00
Agriya KhetarpalandHenry Schreiner 109020ed2d Updates for Pyodide builds after pyodide-build was unvendored (#2090)
* Fix typos and capitalise

* Add docstring for `install_xbuildenv` function

* Include Emscripten, Pyodide version in logger messages

* Mark separation of pyodide-build and Pyodide

* Bump to Pyodide 0.26.4 (py3.12.7) + pyodide-build 0.29.0

* Use pyodide-build version as the source of truth

* Update Pyodide constraints

* Use 3.12 as the Python version instead of 3.12.7

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

* chore: appease the formatter

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2024-11-22 16:17:51 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> fd990007be [pre-commit.ci] pre-commit autoupdate (#2087)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.7.3 → v0.7.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.7.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-11-20 01:18:10 -05:00
Henry SchreinerandMatthieu Darbois e158f22d51 ci: update gitlab for dependency-groups (#2089)
* ci: update gitlab for dependency-groups

* Update .gitlab-ci.yml

* Apply suggestions from code review

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

---------

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2024-11-20 01:17:54 -05:00
8f21eb17be chore: use dependency-groups (#2064)
* chore: use dependency-groups

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* ci: use dep groups in other CIs

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

* chore: read dep groups in noxfile

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

* Add dev environment instructions

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2024-11-19 19:30:06 +01:00
aac31ae2fb docs: fix update scripts usage (#2067)
* Update contributing.md

* Update options.md

* Update options.md

* Update docs/contributing.md

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

* Update contributing.md

* Apply suggestions from code review

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2024-11-18 03:27:05 -05:00
Matthieu Darbois b882b84e47 fix: update the macOS image used on Cirrus CI (#2085)
Use `ghcr.io/cirruslabs/macos-runner:sonoma` instead of `ghcr.io/cirruslabs/macos-sonoma-xcode` which is not supported anymore.
2024-11-18 03:26:39 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 243085f047 [Bot] Update dependencies (#2086)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-11-18 03:26:20 -05:00
Matthieu Darbois df6f886390 feat: add manylinux armv7l (#2052) 2024-11-15 19:07:24 -05:00
Matthieu Darbois 6f21f84a71 fix: capture stderr in utils.call when capture_stdout is True (#2076) 2024-11-14 11:40:59 -05:00
Henry Schreinerandmayeut 393e0352e8 feat: adding enable (#2048)
* feat: adding enable

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* refactor: use recommended term

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: use `cached_property` for `Options` properties

* fix: warn on PyPy default

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2024-11-14 11:39:52 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9459533408 chore(deps): bump actions/attest-build-provenance from 1.4.3 to 1.4.4 in the actions group (#2078)
chore(deps): bump actions/attest-build-provenance in the actions group

Bumps the actions group with 1 update: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/attest-build-provenance` from 1.4.3 to 1.4.4
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/1c608d11d69870c2092266b3f9a6f3abbf17002c...ef244123eb79f2f7a7e75d99086184180e6d0018)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 08:43:41 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> d9f987df66 [pre-commit.ci] pre-commit autoupdate (#2079)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.7.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.7.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-11-12 08:43:18 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> f97c1fb99b [Bot] Update dependencies (#2077)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-11-11 09:15:02 +01:00
Matthieu Darbois 5cdddb2d46 fix: error message when manylinux-interpreters ensure ... failed (#2066)
* review: address review comments from #1630

* fix: error message when `manylinux-interpreters ensure ...` failed

* fix: error message when `manylinux-interpreters ensure ...` failed (option b)
2024-11-09 11:06:13 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> e18a6e9eb0 [pre-commit.ci] pre-commit autoupdate (#2073)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.7.1 → v0.7.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.1...v0.7.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-11-05 01:58:32 -05:00
Aarni Koskela c42b1cecf1 tests: fix CIBW_CONFIG_SETTIGNS typo in test (#2071)
fix: Fix CIBW_CONFIG_SETTIGNS typo in test

Caught with https://github.com/crate-ci/typos
2024-11-05 01:57:51 -05:00
Grzegorz Bokota c940bed415 docs: fix travis link in readme (#2072)
fix travis link in readme
2024-11-05 01:57:17 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 530f52dc9c [Bot] Update dependencies (#2059)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-11-05 07:41:08 +01:00
Henry SchreinerandMatthieu Darbois cc1d977359 feat: support test-groups (#2063)
* feat: support test-groups

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* refactor: address review comments

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* tests: add a integration test

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* Apply suggestions from code review

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

* fix: better error messages based on feedback

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2024-11-04 21:25:01 +01:00
omahs d2c7614c3a docs: fix typos (#2061)
* docs: fix typo

* docs: fix typos

* docs: fix typos
2024-11-01 11:39:32 +00:00
Joe Rickerby 66d45de432 repair-wheel-command docs and error message improvements (#2058)
* Fix bug in docs for running abi3audit in repair-wheel-command on windows

* Better error message for misconfigured repair-wheel-command
2024-10-28 23:22:03 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
eaa861622a [pre-commit.ci] pre-commit autoupdate (#2060)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.7.0 → v0.7.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.0...v0.7.1)
- [github.com/pre-commit/mirrors-mypy: v1.12.1 → v1.13.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.12.1...v1.13.0)

* Update .pre-commit-config.yaml

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2024-10-28 23:17:29 -04:00
Matthieu DarboisandHenry Schreiner 7c3b0fbee8 chore: add warning when running cibuildwheel with python<3.11 (#2050)
* chore: add warning when running cibuildwheel with python<3.11

* fix: use `log.warning` to print warnings in `print_preamble`

* Better warning & doc update

* address review comments

* Update README.md

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2024-10-25 22:07:29 +02:00
Henry Schreiner b98602705f chore: stricter mypy (#2053)
* chore: improve mypy

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore(types): type functions in tests

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* chore(types): No partial types in tests

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-10-22 10:16:59 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
b4d1e17765 [pre-commit.ci] pre-commit autoupdate (#2054)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.0)
- [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.12.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.12.1)

* chore: mypy bug fixed

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-10-22 00:45:06 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 4e93d9153e [Bot] Update dependencies (#2051)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-10-21 10:38:46 -04:00
Matthieu Darbois 1001c837de fix: re-raise subprocess.CalledProcessError when uname -m fails on arm (#2049) 2024-10-19 23:02:40 -04:00
Henry Schreiner 18f54c08e2 docs: print a couple slighly more detailed error messages (#2044)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-10-18 08:52:05 -04:00
Matthieu Darbois 5b7119ecef linux: use Python 3.9 in before_all (#2043)
Also use Python 3.9 for utility python in oci_container.
2024-10-17 01:25:09 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> ddbd67cbde [Bot] Update dependencies (#2040)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-10-15 13:09:49 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 84ac771415 chore(deps): bump wntrblm/nox from 2024.04.15 to 2024.10.09 in the actions group (#2041)
chore(deps): bump wntrblm/nox in the actions group

Bumps the actions group with 1 update: [wntrblm/nox](https://github.com/wntrblm/nox).


Updates `wntrblm/nox` from 2024.04.15 to 2024.10.09
- [Release notes](https://github.com/wntrblm/nox/releases)
- [Changelog](https://github.com/wntrblm/nox/blob/main/CHANGELOG.md)
- [Commits](https://github.com/wntrblm/nox/compare/2024.04.15...2024.10.09)

---
updated-dependencies:
- dependency-name: wntrblm/nox
  dependency-type: direct:production
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-15 13:09:03 +02:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> e792bb9705 [pre-commit.ci] pre-commit autoupdate (#2042)
updates:
- [github.com/python-jsonschema/check-jsonschema: 0.29.3 → 0.29.4](https://github.com/python-jsonschema/check-jsonschema/compare/0.29.3...0.29.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-15 13:08:49 +02:00
Joe Rickerby 7940a4c0e7 Bump version: v2.21.3 2024-10-09 09:04:53 +01:00
Henry Schreiner 3d3159b37b docs: update for 3.13.0 final (#2035)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-10-08 10:26:58 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 0319c431de [Bot] Update dependencies (#2032)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-10-08 01:47:21 -04:00
Matthieu Darbois 80ae9a50c9 fix: do not subclass wheel.bdist_wheel command in ABI3 test (#2030)
fix: do not subclass bdist_wheel command in ABI3 test

With bdist_wheel becoming a first-class citizen command of setuptools, this trick might not work in the future.
2024-10-07 18:26:20 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 006f1e6ec6 [pre-commit.ci] pre-commit autoupdate (#2033)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0)
- [github.com/astral-sh/ruff-pre-commit: v0.6.8 → v0.6.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.8...v0.6.9)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-07 18:25:09 -04:00
Joe Rickerby f185952832 Bump version: v2.21.2 2024-10-02 22:51:53 +01:00
Henry Schreinerandmayeut d5c6a83aeb ci: update job slightly (#2028)
* ci: update job slightly

* Ignore check-wheel-content W002

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2024-10-02 14:52:36 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 2e8e511106 [Bot] Update dependencies (#2029)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-10-02 15:59:31 +02:00
Joe RickerbyHenry SchreinerMatthieu Darboispre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
ec591cdf47 Add note about the planned change of the manylinux default (#1992)
* Add note about the planned change of the manylinux default

* Update docs/options.md

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>

* Change date to 6th May

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

* no manylinux_2_28 pypy_i686 image

* no manylinux_2_28 i686 image

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-02 14:29:22 +02:00
Henry Schreiner ee7fa96f6d chore: remove a couple of things pylint is unhappy about (#2027)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-10-02 00:22:40 -04:00
Henry Schreiner d625d18b91 ci: add 3.13 classifier (#2026)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-10-02 00:22:31 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 895eae340a [Bot] Update dependencies (#2025)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-10-02 00:22:22 -04:00
Matthieu DarboisandHenry Schreiner 9fad66fcfb feat: add musllinux armv7l (#2017)
* feature: add musllinux armv7l

* fix: local armv7l images

* fix python 3.8 tests

* address review comments

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2024-10-01 10:33:57 -04:00
zachlewispre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>mayeut
dfd01af9aa fix: more reliably validate Podman API version (#2016)
* fix: parse version strings that include dashes

It's possible for some container engines to report their versions with a dash (e.g., "4.9.4-rhel"), which breaks packaging.version.Version's ability to parse the string. This commit introduces a version_from_string method which santizies the version string and returns an instance of Version.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* cleanup: pacify ruff

* cleanup: further pacify ruff

* fix: properly define _version_from_string method

Also, lift the method up and prefix with a "_" to better match the existing conventions

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* refactor: more robust podman ver check

Use the "podman --version" command instead of "podman version -f {{json .}}" for better reliability across distributions.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix: oci engine version check

Lower Docker API check to 1.41
Podman versions are not PEP440 compliant, remove distro specific suffixes before parsing.
Add tests with real-world outputs and some made up ones.

* fix: UX on OCIEngineTooOldError

* Add FlexibleVersion

per review comment

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2024-10-01 01:10:58 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 735e88deba [Bot] Update dependencies (#2022)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-10-01 01:09:19 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> bb2f6652b7 [pre-commit.ci] pre-commit autoupdate (#2023)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.6.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.6.8)
- [github.com/python-jsonschema/check-jsonschema: 0.29.2 → 0.29.3](https://github.com/python-jsonschema/check-jsonschema/compare/0.29.2...0.29.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-01 01:08:09 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 6dcd7dd34b [pre-commit.ci] pre-commit autoupdate (#2019)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.6.5 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.5...v0.6.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-24 08:37:07 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 1327af5077 [Bot] Update dependencies (#2018)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-09-23 13:19:50 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 311f64987b [Bot] Update dependencies (#2014)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-09-17 12:12:53 -04:00
Henry Schreiner 08e5b97e57 ci: modernize update workflow (#2010) 2024-09-17 07:54:17 +02:00
Henry Schreiner d4a2945fcc Bump version: v2.21.1 2024-09-16 21:04:46 -04:00
pre-commit-ci[bot] 9913c03128 [pre-commit.ci] pre-commit autoupdate (#2013) 2024-09-16 20:32:04 -04:00
cibuildwheel-bot[bot] c0e28d3401 fix: support uv 0.4.10+ on Linux and update dependencies (#2008) 2024-09-16 20:31:48 -04:00
Matthieu Darbois 8c42e7980c fix: file ownership of files copied into the container (#2007)
Revert to using `cat`/`tar` to copy files/folders into the container.
2024-09-16 23:12:19 +01:00
Henry Schreiner 01ecd4e011 docs: bump pinned versions (#2011)
Trying to fix ci; might need to add `pathspec` if this isn't enough.
2024-09-16 14:01:04 -04:00
Matthieu Darbois 33da1f7d14 fix: do not use uv to setup python on windows when conditions are not met (#2005)
* fix: do not use `uv` to setup python on windows when conditions are not met

* add `can_use_uv` on macOS
2024-09-16 12:03:19 -04:00
Joe Rickerby 79b0dd3287 Bump version: v2.21.0 2024-09-13 16:42:42 +01:00
Matthieu Darbois 0787a44d99 fix: enforce minimum version of docker/podman (#1961)
* fix: enforce minimum version of docker/podman

This allows to always pass `--platform` to the OCI engine
thus fixing issues with multiarch images.

* Allow older versions with warnings

* Upgrade docker on Travis CI

* fix: use `docker cp` instead of `tar`

* Enforce docker>=24.0

* move log to include container.copy_into

* fix: travis-ci, only update docker on aarch64

* skip test_multiarch_image on s390x / ppc64le

* skip flaky test

* chore: only install test deps on Travis CI

* fix: do not try to pull images tagged `cibw_local`

* use "--pull=never" for local images

* Use docker image inspect to check if an image needs to be pulled
2024-09-12 15:32:43 -04:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>mayeut
fd11286290 [Bot] Update dependencies (#1998)
* Update dependencies

* fix: set minimal deployment target to macOS 10.13 with CPython 3.12

---------

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2024-09-12 15:32:22 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 22dc864d60 [pre-commit.ci] pre-commit autoupdate (#2000)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-10 12:22:54 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9d2f46ce4e chore(deps): bump the actions group with 2 updates (#1999)
Bumps the actions group with 2 updates: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) and [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).


Updates `actions/attest-build-provenance` from 1.4.2 to 1.4.3
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/6149ea5740be74af77f260b9db67e633f6b0a9a1...1c608d11d69870c2092266b3f9a6f3abbf17002c)

Updates `peter-evans/create-pull-request` from 6 to 7
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-09 20:04:37 +01:00
105 changed files with 2061 additions and 1017 deletions
+4 -2
View File
@@ -4,11 +4,13 @@ set -o xtrace
if [ "$(uname -s)" == "Darwin" ]; then if [ "$(uname -s)" == "Darwin" ]; then
sudo softwareupdate --install-rosetta --agree-to-license sudo softwareupdate --install-rosetta --agree-to-license
else
docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
fi fi
$PYTHON --version $PYTHON --version
$PYTHON -m venv venv $PYTHON -m venv venv
venv/bin/python -m pip install -U pip venv/bin/python -m pip install -U pip dependency-groups
venv/bin/python -m pip install -e ".[dev]" venv/bin/python -m dependency_groups test | xargs venv/bin/python -m pip install -e.
venv/bin/python -m pip freeze venv/bin/python -m pip freeze
venv/bin/python --version venv/bin/python --version
+6 -3
View File
@@ -2,7 +2,8 @@ only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesI
run_tests: &RUN_TESTS run_tests: &RUN_TESTS
install_cibuildwheel_script: install_cibuildwheel_script:
- python -m pip install -e ".[dev]" pytest-custom-exit-code - python -m pip install dependency-groups
- python -m dependency_groups test | xargs python -m pip install -e.
run_cibuildwheel_tests_script: run_cibuildwheel_tests_script:
- python ./bin/run_tests.py - python ./bin/run_tests.py
@@ -17,6 +18,7 @@ linux_x86_task:
memory: 8G memory: 8G
install_pre_requirements_script: install_pre_requirements_script:
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- apt install -y python3-venv python-is-python3 - apt install -y python3-venv python-is-python3
<<: *RUN_TESTS <<: *RUN_TESTS
@@ -30,6 +32,7 @@ linux_aarch64_task:
memory: 4G memory: 4G
install_pre_requirements_script: install_pre_requirements_script:
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- apt install -y python3-venv python-is-python3 - apt install -y python3-venv python-is-python3
<<: *RUN_TESTS <<: *RUN_TESTS
@@ -50,7 +53,7 @@ windows_x86_task:
macos_arm64_task: macos_arm64_task:
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode image: ghcr.io/cirruslabs/macos-runner:sonoma
env: env:
PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH
@@ -60,7 +63,7 @@ macos_arm64_task:
macos_arm64_cp38_task: macos_arm64_cp38_task:
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode image: ghcr.io/cirruslabs/macos-runner:sonoma
env: env:
PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH
+10 -14
View File
@@ -7,22 +7,17 @@ on:
types: types:
- published - published
permissions: {}
jobs: jobs:
dist: dist:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Build SDist and wheel
run: pipx run build
- uses: actions/upload-artifact@v4
with: with:
name: cibw-sdist persist-credentials: false
path: dist/*
- name: Check metadata - uses: hynek/build-and-inspect-python-package@v2
run: pipx run twine check dist/*
publish: publish:
needs: [dist] needs: [dist]
@@ -36,15 +31,16 @@ jobs:
attestations: write attestations: write
steps: steps:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v8
with: with:
pattern: cibw-* name: Packages
path: dist path: dist
merge-multiple: true
- name: Generate artifact attestation for sdist and wheel - name: Generate artifact attestation for sdist and wheel
uses: actions/attest-build-provenance@6149ea5740be74af77f260b9db67e633f6b0a9a1 # v1.4.2 uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with: with:
subject-path: "dist/cibuildwheel-*" subject-path: "dist/cibuildwheel-*"
- uses: pypa/gh-action-pypi-publish@release/v1 - uses: pypa/gh-action-pypi-publish@release/v1
with:
attestations: true
+52 -37
View File
@@ -4,6 +4,7 @@ on:
push: push:
branches: branches:
- main - main
- 2.x
pull_request: pull_request:
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
@@ -11,6 +12,8 @@ on:
workflow_dispatch: workflow_dispatch:
# allow manual runs on branches without a PR # allow manual runs on branches without a PR
permissions: {}
concurrency: concurrency:
group: test-${{ github.ref }} group: test-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
@@ -20,14 +23,14 @@ jobs:
name: Linters (mypy, flake8, etc.) name: Linters (mypy, flake8, etc.)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-python@v5 with:
persist-credentials: false
- uses: actions/setup-python@v6
id: python id: python
with: with:
python-version: "3.x" python-version: "3.x"
- uses: pre-commit/action@v3.0.1 - uses: j178/prek-action@v1
- name: Check manifest
run: pipx run --python "${{ steps.python.outputs.python-path }}" nox -s check_manifest
- name: PyLint checks - name: PyLint checks
run: pipx run --python "${{ steps.python.outputs.python-path }}" nox -s pylint -- --output-format=github run: pipx run --python "${{ steps.python.outputs.python-path }}" nox -s pylint -- --output-format=github
@@ -37,20 +40,23 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest]
python_version: ['3.12'] python_version: ['3.13']
include: include:
- os: ubuntu-latest - os: ubuntu-latest
python_version: '3.8' python_version: '3.8'
timeout-minutes: 180 timeout-minutes: 180
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-python@v5 with:
persist-credentials: false
- uses: actions/setup-python@v6
name: Install Python ${{ matrix.python_version }} name: Install Python ${{ matrix.python_version }}
with: with:
python-version: ${{ matrix.python_version }} python-version: ${{ matrix.python_version }}
allow-prereleases: true
- uses: yezz123/setup-uv@v4 - uses: astral-sh/setup-uv@v7
# free some space to prevent reaching GHA disk space limits # free some space to prevent reaching GHA disk space limits
- name: Clean docker images - name: Clean docker images
@@ -59,13 +65,18 @@ jobs:
docker system prune -a -f docker system prune -a -f
df -h df -h
# for oci_container unit tests
- name: Set up QEMU
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v4
- name: Install dependencies - name: Install dependencies
run: | run: |
uv pip install --system ".[test]" uv sync --no-dev --group test
- name: Generate a sample project - name: Generate a sample project
run: | run: |
python -m test.test_projects test.test_0_basic.basic_project sample_proj uv run -m test.test_projects test.test_0_basic.basic_project sample_proj
- name: Run a sample build (GitHub Action) - name: Run a sample build (GitHub Action)
uses: ./ uses: ./
@@ -83,7 +94,7 @@ jobs:
with: with:
package-dir: sample_proj package-dir: sample_proj
output-dir: wheelhouse_only output-dir: wheelhouse_only
only: cp312-${{ runner.os == 'Linux' && 'manylinux_x86_64' || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }} only: cp312-${{ runner.os == 'Linux' && (runner.arch == 'ARM64' && 'manylinux_aarch64' || 'manylinux_x86_64') || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }}
- name: Create custom configuration file - name: Create custom configuration file
shell: bash shell: bash
@@ -104,21 +115,21 @@ jobs:
output-dir: wheelhouse_config_file output-dir: wheelhouse_config_file
config-file: sample_proj/cibw.toml config-file: sample_proj/cibw.toml
- name: Check Action artefacts - name: Check Action artifacts
shell: bash shell: bash
run: | run: |
test $(find wheelhouse -name '*.whl' | wc -l) -ge 1 test $(find wheelhouse -name '*.whl' | wc -l) -ge 1
test $(find wheelhouse_only -name '*.whl' | wc -l) -eq 1 test $(find wheelhouse_only -name '*.whl' | wc -l) -eq 1
test $(find wheelhouse_config_file -name '*.whl' | wc -l) -eq 1 test $(find wheelhouse_config_file -name '*.whl' | wc -l) -eq 1
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v7
with: with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: wheelhouse/*.whl path: wheelhouse/*.whl
- name: Test cibuildwheel - name: Test cibuildwheel
run: | run: |
python ./bin/run_tests.py --run-podman uv run bin/run_tests.py ${{ (runner.os == 'Linux' && runner.arch == 'X64') && '--run-podman' || '' }}
emulated-archs: emulated-archs:
name: Get qemu emulated architectures name: Get qemu emulated architectures
@@ -127,16 +138,19 @@ jobs:
outputs: outputs:
archs: ${{ steps.archs.outputs.archs }} archs: ${{ steps.archs.outputs.archs }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-python@v5 with:
persist-credentials: false
- uses: actions/setup-python@v6
with: with:
python-version: "3.x" python-version: "3.x"
- uses: astral-sh/setup-uv@v7
- name: Install dependencies - name: Install dependencies
run: python -m pip install ".[test]" run: uv sync --no-dev --group test
- name: Get qemu emulated architectures - name: Get qemu emulated architectures
id: archs id: archs
run: | run: |
OUTPUT=$(python -c "from json import dumps; from test.utils import EMULATED_ARCHS; print(dumps(EMULATED_ARCHS))") OUTPUT=$(uv run --no-sync python -c "from json import dumps; from test.utils import EMULATED_ARCHS; print(dumps(EMULATED_ARCHS))")
echo "${OUTPUT}" echo "${OUTPUT}"
echo "archs=${OUTPUT}" >> "$GITHUB_OUTPUT" echo "archs=${OUTPUT}" >> "$GITHUB_OUTPUT"
@@ -149,41 +163,43 @@ jobs:
matrix: matrix:
arch: ${{ fromJSON(needs.emulated-archs.outputs.archs) }} arch: ${{ fromJSON(needs.emulated-archs.outputs.archs) }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-python@v5 with:
persist-credentials: false
- uses: actions/setup-python@v6
with: with:
python-version: "3.x" python-version: "3.x"
- uses: astral-sh/setup-uv@v7
- name: Install dependencies - name: Install dependencies
run: python -m pip install ".[test,uv]" run: uv sync --no-dev --group test
- name: Set up QEMU - name: Set up QEMU
id: qemu uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@v3
with:
platforms: all
- name: Run the emulation tests - name: Run the emulation tests
run: pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py run: uv run --no-sync pytest --run-emulation ${{ matrix.arch }} test/test_emulation.py
test-pyodide: test-pyodide:
name: Test cibuildwheel building pyodide wheels name: Test cibuildwheel building Pyodide wheels
needs: lint needs: lint
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
timeout-minutes: 180 timeout-minutes: 180
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-python@v5 with:
persist-credentials: false
- uses: actions/setup-python@v6
name: Install Python 3.12 name: Install Python 3.12
with: with:
python-version: '3.12' python-version: '3.12'
- uses: astral-sh/setup-uv@v7
- name: Install dependencies - name: Install dependencies
run: | run: uv sync --no-dev --group test
python -m pip install ".[test]"
- name: Generate a sample project - name: Generate a sample project
run: | run: |
python -m test.test_projects test.test_0_basic.basic_project sample_proj uv run -m test.test_projects test.test_0_basic.basic_project sample_proj
- name: Run a sample build (GitHub Action) - name: Run a sample build (GitHub Action)
uses: ./ uses: ./
@@ -194,7 +210,6 @@ jobs:
CIBW_PLATFORM: pyodide CIBW_PLATFORM: pyodide
- name: Run tests with 'CIBW_PLATFORM' set to 'pyodide' - name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
run: | run: uv run --no-sync ./bin/run_tests.py
python ./bin/run_tests.py
env: env:
CIBW_PLATFORM: pyodide CIBW_PLATFORM: pyodide
-59
View File
@@ -1,59 +0,0 @@
name: Update dependencies
on:
pull_request:
paths:
- '.github/workflows/update-dependencies.yml'
- 'bin/update_pythons.py'
- 'bin/update_docker.py'
- 'bin/update_virtualenv.py'
- 'bin/projects.py'
- 'docs/data/projects.yml'
- 'noxfile.py'
workflow_dispatch:
schedule:
- cron: '0 6 * * 1' # "At 06:00 on Monday."
jobs:
update-dependencies:
name: Update dependencies
if: github.repository_owner == 'pypa' || github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wntrblm/nox@2024.04.15
with:
python-versions: "3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13"
- name: "Run update: dependencies"
run: nox --force-color -s update_constraints
- name: "Run update: python configs"
run: nox --force-color -s update_pins
- name: "Run update: docs user projects"
run: nox --force-color -s update_proj -- --auth=${{ secrets.GITHUB_TOKEN }}
# we use this step to grab a Github App auth token, so that PRs generated by this workflow
# run the GHA tests.
- uses: tibdex/github-app-token@v2
id: generate-token
if: github.ref == 'refs/heads/main' && github.repository == 'pypa/cibuildwheel'
with:
app_id: ${{ secrets.CIBUILDWHEEL_BOT_APP_ID }}
private_key: ${{ secrets.CIBUILDWHEEL_BOT_APP_PRIVATE_KEY }}
- name: Create Pull Request
if: github.ref == 'refs/heads/main' && github.repository == 'pypa/cibuildwheel'
uses: peter-evans/create-pull-request@v6
with:
commit-message: Update dependencies
title: '[Bot] Update dependencies'
body: |
Update the versions of our dependencies.
PR generated by "Update dependencies" [workflow](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}).
branch: update-dependencies-pr
committer: "cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>"
author: "cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>"
token: ${{ steps.generate-token.outputs.token }}
delete-branch: true
+3 -1
View File
@@ -21,7 +21,9 @@ jobs:
permissions: permissions:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with:
persist-credentials: false
- name: Update the ${{ env.TAG_NAME }} tag - name: Update the ${{ env.TAG_NAME }} tag
id: update-major-minor-tag id: update-major-minor-tag
+8 -4
View File
@@ -15,7 +15,9 @@ linux:
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
script: script:
- curl -sSL https://get.docker.com/ | sh - curl -sSL https://get.docker.com/ | sh
- python -m pip install -e ".[dev]" pytest-custom-exit-code - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- python -m pip install dependency-groups
- python -m dependency_groups test | xargs python -m pip install -e. pytest-custom-exit-code
- python ./bin/run_tests.py - python ./bin/run_tests.py
windows: windows:
@@ -25,17 +27,19 @@ windows:
before_script: before_script:
- choco install python -y --version 3.12.4 - choco install python -y --version 3.12.4
script: script:
- py -m pip install -e ".[dev]" pytest-custom-exit-code - py -m pip install dependency-groups
- py -m pip install -e. pytest-custom-exit-code $(py -m dependency_groups test)
- py bin\run_tests.py - py bin\run_tests.py
tags: tags:
- saas-windows-medium-amd64 - saas-windows-medium-amd64
macos: macos:
image: macos-14-xcode-15 image: macos-latest-xcode-15
variables: variables:
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
script: script:
- python3 -m pip install -e ".[dev]" pytest-custom-exit-code - python3 -m pip install dependency-groups
- python3 -m dependency_groups test | xargs python3 -m pip install -e. pytest-custom-exit-code
- python3 ./bin/run_tests.py - python3 ./bin/run_tests.py
tags: tags:
- saas-macos-medium-m1 - saas-macos-medium-m1
+8 -4
View File
@@ -1,7 +1,7 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 rev: v5.0.0
hooks: hooks:
- id: check-case-conflict - id: check-case-conflict
- id: check-merge-conflict - id: check-merge-conflict
@@ -14,14 +14,14 @@ repos:
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.3 rev: v0.8.0
hooks: hooks:
- id: ruff - id: ruff
args: ["--fix", "--show-fixes"] args: ["--fix", "--show-fixes"]
- id: ruff-format - id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2 rev: v1.13.0
hooks: hooks:
- id: mypy - id: mypy
name: mypy 3.8 on cibuildwheel/ name: mypy 3.8 on cibuildwheel/
@@ -29,9 +29,12 @@ repos:
args: ["--python-version=3.8"] args: ["--python-version=3.8"]
additional_dependencies: &mypy-dependencies additional_dependencies: &mypy-dependencies
- bracex - bracex
- dependency-groups>=1.2
- nox - nox
- orjson
- packaging - packaging
- pygithub - pygithub
- pytest<9
- rich - rich
- tomli - tomli
- tomli_w - tomli_w
@@ -40,6 +43,7 @@ repos:
- types-jinja2 - types-jinja2
- types-pyyaml - types-pyyaml
- types-requests - types-requests
- types-setuptools
- uv - uv
- validate-pyproject - validate-pyproject
- id: mypy - id: mypy
@@ -77,7 +81,7 @@ repos:
- repo: https://github.com/python-jsonschema/check-jsonschema - repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.29.2 rev: 0.29.4
hooks: hooks:
- id: check-dependabot - id: check-dependabot
- id: check-github-actions - id: check-github-actions
+2 -4
View File
@@ -3,13 +3,11 @@
version: 2 version: 2
build: build:
os: ubuntu-22.04 os: ubuntu-24.04
tools: tools:
python: "3.12" python: "3.12"
commands: commands:
- asdf plugin add uv - asdf plugin add uv
- asdf install uv latest - asdf install uv latest
- asdf global uv latest - asdf global uv latest
- uv venv - NO_COLOR=1 uv run --no-dev --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
- uv pip install -e.[docs]
- NO_COLOR=1 .venv/bin/mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
+11 -2
View File
@@ -20,6 +20,14 @@ jobs:
group: edge group: edge
virt: vm virt: vm
env: PYTHON=python env: PYTHON=python
# docker is outdated in the arm64-graviton2 vm focal image (19.x)
# we need to upgrade to get >= 24.0
addons:
apt:
sources:
- sourceline: 'deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable'
packages:
- docker-ce docker-ce-cli containerd.io
- name: Linux | ppc64le | Python 3.9 - name: Linux | ppc64le | Python 3.9
python: 3.9 python: 3.9
@@ -48,8 +56,9 @@ jobs:
env: PYTHON=python env: PYTHON=python
install: install:
- $PYTHON -m pip install -U pip - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all; fi
- $PYTHON -m pip install -e ".[dev]" pytest-custom-exit-code - $PYTHON -m pip install -U pip dependency-groups
- $PYTHON -m dependency_groups test | xargs $PYTHON -m pip install -e.
script: | script: |
# travis_wait disable the output while waiting # travis_wait disable the output while waiting
+44 -65
View File
@@ -4,7 +4,7 @@ cibuildwheel
[![PyPI](https://img.shields.io/pypi/v/cibuildwheel.svg)](https://pypi.python.org/pypi/cibuildwheel) [![PyPI](https://img.shields.io/pypi/v/cibuildwheel.svg)](https://pypi.python.org/pypi/cibuildwheel)
[![Documentation Status](https://readthedocs.org/projects/cibuildwheel/badge/?version=stable)](https://cibuildwheel.pypa.io/en/stable/?badge=stable) [![Documentation Status](https://readthedocs.org/projects/cibuildwheel/badge/?version=stable)](https://cibuildwheel.pypa.io/en/stable/?badge=stable)
[![Actions Status](https://github.com/pypa/cibuildwheel/workflows/Test/badge.svg)](https://github.com/pypa/cibuildwheel/actions) [![Actions Status](https://github.com/pypa/cibuildwheel/workflows/Test/badge.svg)](https://github.com/pypa/cibuildwheel/actions)
[![Travis Status](https://img.shields.io/travis/com/pypa/cibuildwheel/main?logo=travis)](https://travis-ci.com/pypa/cibuildwheel) [![Travis Status](https://img.shields.io/travis/com/pypa/cibuildwheel/main?logo=travis)](https://travis-ci.com/github/pypa/cibuildwheel)
[![Appveyor status](https://ci.appveyor.com/api/projects/status/gt3vwl88yt0y3hur/branch/main?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/main) [![Appveyor status](https://ci.appveyor.com/api/projects/status/gt3vwl88yt0y3hur/branch/main?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/main)
[![CircleCI Status](https://img.shields.io/circleci/build/gh/pypa/cibuildwheel/main?logo=circleci)](https://circleci.com/gh/pypa/cibuildwheel) [![CircleCI Status](https://img.shields.io/circleci/build/gh/pypa/cibuildwheel/main?logo=circleci)](https://circleci.com/gh/pypa/cibuildwheel)
[![Azure Status](https://dev.azure.com/joerick0429/cibuildwheel/_apis/build/status/pypa.cibuildwheel?branchName=main)](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=4&branchName=main) [![Azure Status](https://dev.azure.com/joerick0429/cibuildwheel/_apis/build/status/pypa.cibuildwheel?branchName=main)](https://dev.azure.com/joerick0429/cibuildwheel/_build/latest?definitionId=4&branchName=main)
@@ -22,27 +22,31 @@ Python wheels are great. Building them across **Mac, Linux, Windows**, on **mult
What does it do? What does it do?
---------------- ----------------
| | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | Pyodide | While cibuildwheel itself requires a recent Python version to run (we support the last three releases), it can target the following versions to build wheels:
|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|-----|
| CPython 3.6 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | | | macOS Intel | macOS Apple Silicon | Windows 64bit | Windows 32bit | Windows Arm64 | manylinux<br/>musllinux x86_64 | manylinux<br/>musllinux i686 | manylinux<br/>musllinux aarch64 | manylinux<br/>musllinux ppc64le | manylinux<br/>musllinux s390x | manylinux<br/>musllinux armv7l | Pyodide |
| CPython 3.7 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | |----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|---|-----|
| CPython 3.8 | ✅ | | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | | CPython 3.6 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.9 | ✅ | | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | | CPython 3.7 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | | CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | | CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| CPython 3.1 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | | CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| PyPy 3.7 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | | CPython 3.12 | ✅ | | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅⁴ |
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | | CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A |
| PyPy 3.9 v7.3 | ✅ | | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | | PyPy 3.7 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | | PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A |
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A |
| PyPy 3.11 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A | N/A | N/A |
<sup>¹ PyPy is only supported for manylinux wheels.</sup><br> <sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
<sup>² Windows arm64 support is experimental.</sup><br> <sup>² Windows arm64 support is experimental.</sup><br>
<sup>³ CPython 3.13 is built by default using Python RCs, starting with cibuildwheel 2.20. Free-threaded mode will still require opt-in using [`CIBW_FREE_THREADED_SUPPORT`](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support).</sup><br> <sup>³ Free-threaded mode requires opt-in using [`CIBW_FREE_THREADED_SUPPORT`](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support).</sup><br>
<sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br> <sup>⁴ Experimental, not yet supported on PyPI, but can be used directly in web deployment. Use `--platform pyodide` to build.</sup><br>
<sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>
- Builds manylinux, musllinux, macOS 10.9+, and Windows wheels for CPython and PyPy - Builds manylinux, musllinux, macOS 10.9+ (10.13+ for Python 3.12+), and Windows wheels for CPython and PyPy
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI - Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate) - Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
- Runs your library's tests against the wheel-installed version of your library - Runs your library's tests against the wheel-installed version of your library
@@ -56,7 +60,7 @@ Usage
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | | | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM |
|-----------------|-------|-------|---------|-----------|-----------|-------------| |-----------------|-------|-------|---------|-----------|-----------|-------------|
| GitHub Actions | ✅ | ✅ | ✅ | ✅¹ | ✅ | ✅² | | GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | | Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² |
| Travis CI | ✅ | | ✅ | ✅ | | | | Travis CI | ✅ | | ✅ | ✅ | | |
| AppVeyor | ✅ | ✅ | ✅ | | ✅ | ✅² | | AppVeyor | ✅ | ✅ | ✅ | | ✅ | ✅² |
@@ -85,7 +89,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -94,7 +98,7 @@ jobs:
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
- name: Install cibuildwheel - name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.20.0 run: python -m pip install cibuildwheel==2.23.4
- name: Build wheels - name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse run: python -m cibuildwheel --output-dir wheelhouse
@@ -211,65 +215,40 @@ Changelog
<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually --> <!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
### v2.20.0 ### v2.23.4
- 🌟 CPython 3.13 wheels are now built by default - without the `CIBW_PRERELEASE_PYTHONS` flag. It's time to build and upload these wheels to PyPI! This release includes CPython 3.13.0rc1, which is guaranteed to be ABI compatible with the final release. Free-threading is still behind a flag/config option. (#1950) _16 March 2026_
- ✨ Provide a `CIBW_ALLOW_EMPTY` environment variable as an alternative to the command line flag. (#1937)
- 🐛 Don't use uv on PyPy3.8 on Windows, it stopped working starting in 0.2.25. Note that PyPy 3.8 is EoL. (#1868)
- 🛠 Set the `VSCMD_ARG_TGT_ARCH` variable based on target arch. (#1876)
- 🛠 Undo cleaner output on pytest 8-8.2 now that 8.3 is out. (#1943)
- 📚 Update examples to use Python 3.12 on host (cibuildwheel will require Python 3.11+ on the host machine starting in October 2024) (#1919)
- 🐛 Fix HTTP 429 errors while downloading virtualenv from GitHub blob URLs uses a https://bootstrap.pypa.io/ URL instead. (#2775)
### v2.19.2 ### v2.23.3
- 🐛 Update manylinux2014 pins to versions that support past-EoL CentOS 7 mirrors. (#1917) _26 April 2025_
- 🐛 Support `--no-isolation` with `build[uv]` build-frontend. (#1889)
- 🛠 Provide attestations for releases at <https://github.com/pypa/cibuildwheel/attestations>. (#1916)
- 🛠 Provide CPython 3.13.0b3. (#1913)
- 🛠 Remove some workarounds now that pip 21.1 is available. (#1891, #1892)
- 📚 Remove nosetest from our docs. (#1821)
- 📚 Document the macOS ARM workaround for 3.8 on GHA. (#1871)
- 📚 GitLab CI + macOS is now a supported platform with an example. (#1911)
- 🛠 Dependency updates, including Python 3.13.3 (#2371)
### v2.19.1 ### v2.23.2
- 🐛 Don't require setup-python on GHA for Pyodide (#1868) _24 March 2025_
- 🐛 Specify full python path for uv (fixes issue in 0.2.10 & 0.2.11) (#1881)
- 🛠 Update for pip 24.1b2 on CPython 3.13. (#1879)
- 🛠 Fix a warning in our schema generation script. (#1866)
- 🛠 Cleaner output on pytest 8-8.2. (#1865)
- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
### v2.19.0 ### v2.23.1
See the [release post](https://iscinumpy.dev/post/cibuildwheel-2-19-0/) for more info on new features! _15 March 2025_
- 🌟 Add Pyodide platform. Set with `--platform pyodide` or `CIBW_PLATFORM: pyodide` on Linux with a host Python 3.12 to build WebAssembly wheels. Not accepted on PyPI currently, but usable directly in a website using Pyodide, for live docs, etc. (#1456, #1859) - ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
- 🌟 Add `build[uv]` backend, which will take a pre-existing uv install (or install `cibuildwheel[uv]`) and use `uv` for all environment setup and installs on Python 3.8+. This is significantly faster in most cases. (#1856) - 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
- ✨ Add free-threaded macOS builds and update CPython to 3.13.0b2. (#1854)
- 🐛 Issue copying a wheel to a non-existent output dir fixed. (#1851, #1862)
- 🐛 Better determinism for the test environment seeding. (#1835)
- 🛠 `VIRTUAL_ENV` variable now set. (#1842)
- 🛠 Remove a pip<21.3 workaround. (#1842)
- 🛠 Error handling was refactored to use exceptions. (#1719)
- 🛠 Hardcoded paths in tests avoided. (#1834)
- 🛠 Single Python tests made more generic. (#1835)
- 🛠 Sped up our ci by splitting up emulation tests. (#1839)
### v2.23.0
_1 March 2025_
### v2.18.1 - ✨ Adds official support for the new GitHub Actions Arm runners. In fact these worked out-of-the-box, now we include them in our tests and example configs. (#2135 via #2281)
- ✨ Adds support for building PyPy 3.11 wheels (#2268 via #2281)
- 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831) - 🛠 Adopts the beta pypa/manylinux image for armv7l builds (#2269 via #2281)
- ✨ The `container-engine` is now a build (non-global) option. (#1792) - 🛠 Dependency updates, including Pyodide 0.27 (#2117 and #2281)
- 🛠 The build backend for cibuildwheel is now hatchling. (#1297)
- 🛠 Significant improvements and modernization to our noxfile. (#1823)
- 🛠 Use pylint's new GitHub Actions reporter instead of a custom matcher. (#1823)
- 🛠 Unpin virtualenv updates for Python 3.7+ (#1830)
- 🐛 Fix running linux tests from Windows or macOS ARM. (#1788)
- 📚 Fix our documentation build. (#1821)
<!-- END bin/update_readme_changelog.py --> <!-- END bin/update_readme_changelog.py -->
+3
View File
@@ -17,6 +17,9 @@ init:
if (-not ($BRANCH -eq 'main' -or $BRANCH.ToLower().StartsWith('appveyor-'))) { if (-not ($BRANCH -eq 'main' -or $BRANCH.ToLower().StartsWith('appveyor-'))) {
$env:PYTEST_ADDOPTS = '-k "unit_test or test_0_basic" --suppress-no-test-exit-code' $env:PYTEST_ADDOPTS = '-k "unit_test or test_0_basic" --suppress-no-test-exit-code'
} }
if ($IsLinux) {
docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
}
install: install:
- python -m pip install -U pip - python -m pip install -U pip
+10 -6
View File
@@ -7,32 +7,36 @@ pr:
jobs: jobs:
- job: linux_38 - job: linux_38
timeoutInMinutes: 120 timeoutInMinutes: 120
pool: {vmImage: 'Ubuntu-20.04'} pool: {vmImage: 'ubuntu-latest'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.8' versionSpec: '3.8'
- bash: | - bash: |
python -m pip install -e ".[dev]" docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
python -m pip install dependency-groups
python -m dependency_groups test | xargs python -m pip install -e.
python ./bin/run_tests.py python ./bin/run_tests.py
- job: macos_38 - job: macos_38
pool: {vmImage: 'macOS-12'} pool: {vmImage: 'macos-latest'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.8' versionSpec: '3.8'
- bash: | - bash: |
python -m pip install -e ".[dev]" python -m pip install dependency-groups
python -m dependency_groups test | xargs python -m pip install -e.
python ./bin/run_tests.py --num-processes 2 python ./bin/run_tests.py --num-processes 2
- job: windows_38 - job: windows_38
pool: {vmImage: 'windows-2019'} pool: {vmImage: 'windows-latest'}
timeoutInMinutes: 180 timeoutInMinutes: 180
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.8' versionSpec: '3.8'
- bash: | - bash: |
python -m pip install -e ".[dev]" python -m pip install dependency-groups
python -m dependency_groups test | xargs python -m pip install -e.
python ./bin/run_tests.py python ./bin/run_tests.py
+24
View File
@@ -26,6 +26,12 @@ $defs:
- append - append
default: none default: none
description: How to inherit the parent's value. description: How to inherit the parent's value.
enable:
enum:
- cpython-freethreading
- cpython-prerelease
- pypy
description: A Python version or flavor to enable.
additionalProperties: false additionalProperties: false
description: cibuildwheel's settings. description: cibuildwheel's settings.
type: object type: object
@@ -99,6 +105,13 @@ properties:
default: pinned default: pinned
description: Specify how cibuildwheel controls the versions of the tools it uses description: Specify how cibuildwheel controls the versions of the tools it uses
type: string type: string
enable:
description: Enable or disable certain builds.
oneOf:
- $ref: "#/$defs/enable"
- type: array
items:
$ref: "#/$defs/enable"
environment: environment:
description: Set environment variables needed during the build. description: Set environment variables needed during the build.
type: string_table type: string_table
@@ -110,9 +123,13 @@ properties:
type: boolean type: boolean
default: false default: false
description: The project supports free-threaded builds of Python (PEP703) description: The project supports free-threaded builds of Python (PEP703)
deprecated: Use the `enable` option instead.
manylinux-aarch64-image: manylinux-aarch64-image:
type: string type: string
description: Specify alternative manylinux / musllinux container images description: Specify alternative manylinux / musllinux container images
manylinux-armv7l-image:
type: string
description: Specify alternative manylinux / musllinux container images
manylinux-i686-image: manylinux-i686-image:
type: string type: string
description: Specify alternative manylinux / musllinux container images description: Specify alternative manylinux / musllinux container images
@@ -137,6 +154,9 @@ properties:
musllinux-aarch64-image: musllinux-aarch64-image:
type: string type: string
description: Specify alternative manylinux / musllinux container images description: Specify alternative manylinux / musllinux container images
musllinux-armv7l-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-i686-image: musllinux-i686-image:
type: string type: string
description: Specify alternative manylinux / musllinux container images description: Specify alternative manylinux / musllinux container images
@@ -161,6 +181,9 @@ properties:
test-extras: test-extras:
description: Install your wheel for testing using `extras_require` description: Install your wheel for testing using `extras_require`
type: string_array type: string_array
test-groups:
description: Install extra groups when testing
type: string_array
test-requires: test-requires:
description: Install Python dependencies before running the tests description: Install Python dependencies before running the tests
type: string_array type: string_array
@@ -255,6 +278,7 @@ del non_global_options["build"]
del non_global_options["skip"] del non_global_options["skip"]
del non_global_options["test-skip"] del non_global_options["test-skip"]
del non_global_options["free-threaded-support"] del non_global_options["free-threaded-support"]
del non_global_options["enable"]
overrides["items"]["properties"]["select"]["oneOf"] = string_array overrides["items"]["properties"]["select"]["oneOf"] = string_array
overrides["items"]["properties"] |= non_global_options.copy() overrides["items"]["properties"] |= non_global_options.copy()
+10 -8
View File
@@ -3,27 +3,29 @@
from __future__ import annotations from __future__ import annotations
import os import os
import subprocess
import sys import sys
import textwrap import textwrap
import time import time
from pathlib import Path from pathlib import Path
from subprocess import run
import click import click
def shell(cmd, *, check: bool, **kwargs): def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
return run([cmd], shell=True, check=check, **kwargs) return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]
def git_repo_has_changes(): def git_repo_has_changes() -> bool:
unstaged_changes = shell("git diff-index --quiet HEAD --", check=False).returncode != 0 unstaged_changes: bool = shell("git diff-index --quiet HEAD --", check=False).returncode != 0
staged_changes = shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0 staged_changes: bool = (
shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0
)
return unstaged_changes or staged_changes return unstaged_changes or staged_changes
@click.command() @click.command()
def main(): def main() -> None:
project_root = Path(__file__).parent / ".." project_root = Path(__file__).parent / ".."
os.chdir(project_root) os.chdir(project_root)
@@ -57,7 +59,7 @@ def main():
PR generated by `{os.path.basename(__file__)}`. PR generated by `{os.path.basename(__file__)}`.
""" """
) )
run( subprocess.run(
[ [
"gh", "gh",
"pr", "pr",
+4 -2
View File
@@ -172,7 +172,9 @@ def get_projects(
return sorted((Project(item, github) for item in config), reverse=online) return sorted((Project(item, github) for item in config), reverse=online)
def render_projects(projects: Sequence[Project], *, dest_path: Path, include_info: bool = True): def render_projects(
projects: Sequence[Project], *, dest_path: Path, include_info: bool = True
) -> str:
io = StringIO() io = StringIO()
print = functools.partial(builtins.print, file=io) print = functools.partial(builtins.print, file=io)
@@ -203,7 +205,7 @@ def insert_projects_table(
projects: Sequence[Project], projects: Sequence[Project],
input_filename: str, input_filename: str,
include_info: bool = True, include_info: bool = True,
): ) -> None:
text = file.read_text() text = file.read_text()
projects_table = render_projects(projects, include_info=include_info, dest_path=file) projects_table = render_projects(projects, include_info=include_info, dest_path=file)
+8 -8
View File
@@ -4,29 +4,29 @@ from __future__ import annotations
import os import os
import shutil import shutil
import subprocess
import sys import sys
import textwrap import textwrap
import time import time
import typing import typing
from glob import glob from glob import glob
from pathlib import Path from pathlib import Path
from subprocess import run
from urllib.parse import quote from urllib.parse import quote
import click import click
def shell(cmd, *, check: bool, **kwargs): def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
return run([cmd], shell=True, check=check, **kwargs) return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]
def git_repo_has_changes(): def git_repo_has_changes() -> bool:
unstaged_changes = shell("git diff-index --quiet HEAD --", check=False).returncode != 0 unstaged_changes = shell("git diff-index --quiet HEAD --", check=False).returncode != 0
staged_changes = shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0 staged_changes = shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0
return unstaged_changes or staged_changes return unstaged_changes or staged_changes
def generate_basic_project(path): def generate_basic_project(path: Path) -> None:
sys.path.insert(0, "") sys.path.insert(0, "")
from test.test_projects.c import new_c_project from test.test_projects.c import new_c_project
@@ -79,7 +79,7 @@ services = [
] ]
def ci_service_for_config_file(config_file): def ci_service_for_config_file(config_file: str) -> CIService:
filename = Path(config_file).name filename = Path(config_file).name
try: try:
@@ -134,7 +134,7 @@ def run_example_ci_configs(config_files=None):
dst_config_file.parent.mkdir(parents=True, exist_ok=True) dst_config_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(src_config_file, dst_config_file) shutil.copyfile(src_config_file, dst_config_file)
run(["git", "add", example_project], check=True) subprocess.run(["git", "add", example_project], check=True)
message = textwrap.dedent( message = textwrap.dedent(
f"""\ f"""\
Test example minimal configs Test example minimal configs
@@ -144,7 +144,7 @@ def run_example_ci_configs(config_files=None):
Time: {timestamp} Time: {timestamp}
""" """
) )
run(["git", "commit", "--no-verify", "--message", message], check=True) subprocess.run(["git", "commit", "--no-verify", "--message", message], check=True)
shell(f"git subtree --prefix={example_project} push origin {branch_name}", check=True) shell(f"git subtree --prefix={example_project} push origin {branch_name}", check=True)
print("---") print("---")
+1 -1
View File
@@ -28,7 +28,7 @@ if __name__ == "__main__":
# unit tests # unit tests
unit_test_args = [sys.executable, "-m", "pytest", "unit_test"] unit_test_args = [sys.executable, "-m", "pytest", "unit_test"]
if sys.platform.startswith("linux"): if sys.platform.startswith("linux") and os.environ.get("CIBW_PLATFORM", "linux") == "linux":
# run the docker unit tests only on Linux # run the docker unit tests only on Linux
unit_test_args += ["--run-docker"] unit_test_args += ["--run-docker"]
+19
View File
@@ -6,6 +6,7 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
import requests import requests
from packaging.version import Version
DIR = Path(__file__).parent.resolve() DIR = Path(__file__).parent.resolve()
RESOURCES = DIR.parent / "cibuildwheel/resources" RESOURCES = DIR.parent / "cibuildwheel/resources"
@@ -53,6 +54,8 @@ images = [
Image("manylinux_2_28", "s390x", "quay.io/pypa/manylinux_2_28_s390x", None), Image("manylinux_2_28", "s390x", "quay.io/pypa/manylinux_2_28_s390x", None),
Image("manylinux_2_28", "pypy_x86_64", "quay.io/pypa/manylinux_2_28_x86_64", None), Image("manylinux_2_28", "pypy_x86_64", "quay.io/pypa/manylinux_2_28_x86_64", None),
Image("manylinux_2_28", "pypy_aarch64", "quay.io/pypa/manylinux_2_28_aarch64", None), Image("manylinux_2_28", "pypy_aarch64", "quay.io/pypa/manylinux_2_28_aarch64", None),
# manylinux_2_31 images
Image("manylinux_2_31", "armv7l", "quay.io/pypa/manylinux_2_31_armv7l", None),
# musllinux_1_1 images # musllinux_1_1 images
Image("musllinux_1_1", "x86_64", "quay.io/pypa/musllinux_1_1_x86_64", None), Image("musllinux_1_1", "x86_64", "quay.io/pypa/musllinux_1_1_x86_64", None),
Image("musllinux_1_1", "i686", "quay.io/pypa/musllinux_1_1_i686", None), Image("musllinux_1_1", "i686", "quay.io/pypa/musllinux_1_1_i686", None),
@@ -65,6 +68,7 @@ images = [
Image("musllinux_1_2", "aarch64", "quay.io/pypa/musllinux_1_2_aarch64", None), Image("musllinux_1_2", "aarch64", "quay.io/pypa/musllinux_1_2_aarch64", None),
Image("musllinux_1_2", "ppc64le", "quay.io/pypa/musllinux_1_2_ppc64le", None), Image("musllinux_1_2", "ppc64le", "quay.io/pypa/musllinux_1_2_ppc64le", None),
Image("musllinux_1_2", "s390x", "quay.io/pypa/musllinux_1_2_s390x", None), Image("musllinux_1_2", "s390x", "quay.io/pypa/musllinux_1_2_s390x", None),
Image("musllinux_1_2", "armv7l", "quay.io/pypa/musllinux_1_2_armv7l", None),
] ]
config = configparser.ConfigParser() config = configparser.ConfigParser()
@@ -90,6 +94,21 @@ for image in images:
for (name, info) in tags_dict.items() for (name, info) in tags_dict.items()
if info["manifest_digest"] == latest_tag["manifest_digest"] if info["manifest_digest"] == latest_tag["manifest_digest"]
) )
elif image.image_name.startswith("ghcr.io/"):
repository = image.image_name[8:]
response = requests.get(
"https://ghcr.io/token", params={"scope": f"repository:{repository}:pull"}
)
response.raise_for_status()
token = response.json()["token"]
response = requests.get(
f"https://ghcr.io/v2/{repository}/tags/list",
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
ghcr_tags = [(Version(tag), tag) for tag in response.json()["tags"] if tag != "latest"]
ghcr_tags.sort(reverse=True)
tag_name = ghcr_tags[0][1]
else: else:
response = requests.get(f"https://hub.docker.com/v2/repositories/{image.image_name}/tags") response = requests.get(f"https://hub.docker.com/v2/repositories/{image.image_name}/tags")
response.raise_for_status() response.raise_for_status()
+1 -1
View File
@@ -18,7 +18,7 @@ except ImportError:
) )
def main(): def main() -> None:
subprocess.run(["mkdocs", "build"], check=True) subprocess.run(["mkdocs", "build"], check=True)
hti = Html2Image(custom_flags=["--force-device-scale-factor=2"]) hti = Html2Image(custom_flags=["--force-device-scale-factor=2"])
+1 -1
View File
@@ -20,7 +20,7 @@ README_CHANGELOG_SECTION = re.compile(
) )
def main(): def main() -> None:
changelog_text = CHANGELOG_FILE.read_text() changelog_text = CHANGELOG_FILE.read_text()
readme_text = README_FILE.read_text() readme_text = README_FILE.read_text()
+1 -1
View File
@@ -36,7 +36,7 @@ class VersionTuple:
version_string: str version_string: str
def git_ls_remote_versions(url) -> list[VersionTuple]: def git_ls_remote_versions(url: str) -> list[VersionTuple]:
versions: list[VersionTuple] = [] versions: list[VersionTuple] = []
tags = subprocess.run( tags = subprocess.run(
["git", "ls-remote", "--tags", url], check=True, text=True, capture_output=True ["git", "ls-remote", "--tags", url], check=True, text=True, capture_output=True
+1 -1
View File
@@ -1,3 +1,3 @@
from __future__ import annotations from __future__ import annotations
__version__ = "2.20.0" __version__ = "2.23.4"
+15 -7
View File
@@ -271,7 +271,6 @@ class PlatformModule(Protocol):
def build(self, options: Options, tmp_path: Path) -> None: ... def build(self, options: Options, tmp_path: Path) -> None: ...
# pylint: disable-next=inconsistent-return-statements
def get_platform_module(platform: PlatformName) -> PlatformModule: def get_platform_module(platform: PlatformName) -> PlatformModule:
if platform == "linux": if platform == "linux":
return cibuildwheel.linux return cibuildwheel.linux
@@ -381,14 +380,13 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str])
print() print()
print(f"Cache folder: {CIBW_CACHE_PATH}") print(f"Cache folder: {CIBW_CACHE_PATH}")
print()
warnings = detect_warnings(options=options, identifiers=identifiers) warnings = detect_warnings(options=options, identifiers=identifiers)
if warnings: for warning in warnings:
print("\nWarnings:") log.warning(warning)
for warning in warnings:
print(" " + warning)
print("\nHere we go!\n") print("Here we go!\n")
def get_build_identifiers( def get_build_identifiers(
@@ -403,6 +401,16 @@ def get_build_identifiers(
def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]: def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]:
warnings = [] warnings = []
python_version_deprecation = ((3, 11), 3)
if sys.version_info[:2] < python_version_deprecation[0]:
python_version = ".".join(map(str, python_version_deprecation[0]))
msg = (
f"cibuildwheel {python_version_deprecation[1]} will require Python {python_version}+, "
"please upgrade the Python version used to run cibuildwheel. "
"This does not affect the versions you can target when building wheels. See: https://cibuildwheel.pypa.io/en/stable/#what-does-it-do"
)
warnings.append(msg)
# warn about deprecated {python} and {pip} # warn about deprecated {python} and {pip}
for option_name in ["test_command", "before_build"]: for option_name in ["test_command", "before_build"]:
option_values = [getattr(options.build_options(i), option_name) for i in identifiers] option_values = [getattr(options.build_options(i), option_name) for i in identifiers]
@@ -411,7 +419,7 @@ def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str
# Reminder: in an f-string, double braces means literal single brace # Reminder: in an f-string, double braces means literal single brace
msg = ( msg = (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, " f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
"and will be removed in a future release. Simply use 'python' or 'pip' instead." "and will be removed in cibuildwheel 3. Simply use 'python' or 'pip' instead."
) )
warnings.append(msg) warnings.append(msg)
+1 -1
View File
@@ -8,7 +8,7 @@ else:
from typing import NotRequired, Self, assert_never from typing import NotRequired, Self, assert_never
__all__ = ( __all__ = (
"assert_never",
"NotRequired", "NotRequired",
"Self", "Self",
"assert_never",
) )
+8 -6
View File
@@ -37,6 +37,7 @@ class Architecture(Enum):
aarch64 = "aarch64" aarch64 = "aarch64"
ppc64le = "ppc64le" ppc64le = "ppc64le"
s390x = "s390x" s390x = "s390x"
armv7l = "armv7l"
# mac archs # mac archs
universal2 = "universal2" universal2 = "universal2"
@@ -64,7 +65,9 @@ class Architecture(Enum):
if arch_str == "auto": if arch_str == "auto":
result |= Architecture.auto_archs(platform=platform) result |= Architecture.auto_archs(platform=platform)
elif arch_str == "native": elif arch_str == "native":
result.add(Architecture(platform_module.machine())) native_arch = Architecture.native_arch(platform=platform)
if native_arch:
result.add(native_arch)
elif arch_str == "all": elif arch_str == "all":
result |= Architecture.all_archs(platform=platform) result |= Architecture.all_archs(platform=platform)
elif arch_str == "auto64": elif arch_str == "auto64":
@@ -130,6 +133,7 @@ class Architecture(Enum):
Architecture.aarch64, Architecture.aarch64,
Architecture.ppc64le, Architecture.ppc64le,
Architecture.s390x, Architecture.s390x,
Architecture.armv7l,
}, },
"macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2}, "macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
"windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64}, "windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
@@ -138,17 +142,15 @@ class Architecture(Enum):
return all_archs_map[platform] return all_archs_map[platform]
@staticmethod @staticmethod
# pylint: disable-next=inconsistent-return-statements
def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> set[Architecture]: def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> set[Architecture]:
archs_32 = {Architecture.i686, Architecture.x86} archs_32 = {Architecture.i686, Architecture.x86, Architecture.armv7l}
auto_archs = Architecture.auto_archs(platform) auto_archs = Architecture.auto_archs(platform)
if bitness == "64": if bitness == "64":
return auto_archs - archs_32 return auto_archs - archs_32
elif bitness == "32": if bitness == "32":
return auto_archs & archs_32 return auto_archs & archs_32
else: assert_never(bitness)
assert_never(bitness)
def allowed_architectures_check( def allowed_architectures_check(
+31 -4
View File
@@ -35,11 +35,14 @@ class NonPlatformWheelError(FatalError):
""" """
Build failed because a pure Python wheel was generated. Build failed because a pure Python wheel was generated.
If you intend to build a pure-Python wheel, you don't need cibuildwheel - use If you intend to build a pure-Python wheel, you don't need
`pip wheel -w DEST_DIR .` instead. cibuildwheel - use `pip wheel .`, `pipx run build --wheel`, `uv
build --wheel`, etc. instead. You only need cibuildwheel if you
have compiled (not Python) code in your wheels making them depend
on the platform.
If you expected a platform wheel, check your project configuration, or run If you expected a platform wheel, check your project configuration,
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs. or run cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
""" """
) )
super().__init__(message) super().__init__(message)
@@ -58,3 +61,27 @@ class AlreadyBuiltWheelError(FatalError):
) )
super().__init__(message) super().__init__(message)
self.return_code = 6 self.return_code = 6
class OCIEngineTooOldError(FatalError):
def __init__(self, message: str) -> None:
super().__init__(message)
self.return_code = 7
class RepairStepProducedNoWheelError(FatalError):
def __init__(self) -> None:
message = textwrap.dedent(
"""
Build failed because the repair step completed successfully but
did not produce a wheel.
Your `repair-wheel-command` is expected to place the repaired
wheel in the {dest_dir} directory. See the documentation for
example configurations:
https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command
"""
)
super().__init__(message)
self.return_code = 8
+39 -18
View File
@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import contextlib
import subprocess import subprocess
import sys import sys
import textwrap import textwrap
@@ -14,7 +15,7 @@ from . import errors
from ._compat.typing import assert_never from ._compat.typing import assert_never
from .architecture import Architecture from .architecture import Architecture
from .logger import log from .logger import log
from .oci_container import OCIContainer, OCIContainerEngineConfig from .oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
from .options import BuildOptions, Options from .options import BuildOptions, Options
from .typing import PathOrStr from .typing import PathOrStr
from .util import ( from .util import (
@@ -29,6 +30,15 @@ from .util import (
unwrap, unwrap,
) )
ARCHITECTURE_OCI_PLATFORM_MAP = {
Architecture.x86_64: OCIPlatform.AMD64,
Architecture.i686: OCIPlatform.i386,
Architecture.aarch64: OCIPlatform.ARM64,
Architecture.ppc64le: OCIPlatform.PPC64LE,
Architecture.s390x: OCIPlatform.S390X,
Architecture.armv7l: OCIPlatform.ARMV7,
}
@dataclass(frozen=True) @dataclass(frozen=True)
class PythonConfiguration: class PythonConfiguration:
@@ -124,26 +134,33 @@ def check_all_python_exist(
*, platform_configs: Iterable[PythonConfiguration], container: OCIContainer *, platform_configs: Iterable[PythonConfiguration], container: OCIContainer
) -> None: ) -> None:
exist = True exist = True
has_manylinux_interpreters = True has_manylinux_interpreters = False
messages = [] messages = []
try: with contextlib.suppress(subprocess.CalledProcessError):
# use capture_output to keep quiet # use capture_output to keep quiet
container.call(["manylinux-interpreters", "--help"], capture_output=True) container.call(["manylinux-interpreters", "--help"], capture_output=True)
except subprocess.CalledProcessError: has_manylinux_interpreters = True
has_manylinux_interpreters = False
for config in platform_configs: for config in platform_configs:
python_path = config.path / "bin" / "python" python_path = config.path / "bin" / "python"
try: if has_manylinux_interpreters:
if has_manylinux_interpreters: try:
container.call(["manylinux-interpreters", "ensure", config.path.name]) container.call(["manylinux-interpreters", "ensure", config.path.name])
container.call(["test", "-x", python_path]) except subprocess.CalledProcessError:
except subprocess.CalledProcessError: messages.append(
messages.append( f" 'manylinux-interpreters ensure {config.path.name}' needed to build '{config.identifier}' failed in container running image '{container.image}'."
f" '{python_path}' executable doesn't exist in image '{container.image}' to build '{config.identifier}'." " Either the installation failed or this interpreter is not available in that image. Please check the logs."
) )
exist = False exist = False
else:
try:
container.call(["test", "-x", python_path])
except subprocess.CalledProcessError:
messages.append(
f" '{python_path}' executable doesn't exist in image '{container.image}' to build '{config.identifier}'."
)
exist = False
if not exist: if not exist:
message = "\n".join(messages) message = "\n".join(messages)
raise errors.FatalError(message) raise errors.FatalError(message)
@@ -171,7 +188,7 @@ def build_in_container(
log.step("Running before_all...") log.step("Running before_all...")
env = container.get_environment() env = container.get_environment()
env["PATH"] = f'/opt/python/cp38-cp38/bin:{env["PATH"]}' env["PATH"] = f'/opt/python/cp39-cp39/bin:{env["PATH"]}'
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1" env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
env["PIP_ROOT_USER_ACTION"] = "ignore" env["PIP_ROOT_USER_ACTION"] = "ignore"
env = before_all_options.environment.as_dictionary( env = before_all_options.environment.as_dictionary(
@@ -196,6 +213,8 @@ def build_in_container(
dependency_constraint_flags: list[PathOrStr] = [] dependency_constraint_flags: list[PathOrStr] = []
log.step("Setting up build environment...")
if build_options.dependency_constraints: if build_options.dependency_constraints:
constraints_file = build_options.dependency_constraints.get_for_python_version( constraints_file = build_options.dependency_constraints.get_for_python_version(
config.version config.version
@@ -205,8 +224,6 @@ def build_in_container(
container.copy_into(constraints_file, container_constraints_file) container.copy_into(constraints_file, container_constraints_file)
dependency_constraint_flags = ["-c", container_constraints_file] dependency_constraint_flags = ["-c", container_constraints_file]
log.step("Setting up build environment...")
env = container.get_environment() env = container.get_environment()
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1" env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
env["PIP_ROOT_USER_ACTION"] = "ignore" env["PIP_ROOT_USER_ACTION"] = "ignore"
@@ -317,6 +334,9 @@ def build_in_container(
repaired_wheels = container.glob(repaired_wheel_dir, "*.whl") repaired_wheels = container.glob(repaired_wheel_dir, "*.whl")
if not repaired_wheels:
raise errors.RepairStepProducedNoWheelError()
for repaired_wheel in repaired_wheels: for repaired_wheel in repaired_wheels:
if repaired_wheel.name in {wheel.name for wheel in built_wheels}: if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name) raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
@@ -337,7 +357,7 @@ def build_in_container(
venv_dir = testing_temp_dir / "venv" venv_dir = testing_temp_dir / "venv"
if use_uv: if use_uv:
container.call(["uv", "venv", venv_dir], env=env) container.call(["uv", "venv", venv_dir, "--python", python_bin / "python"], env=env)
else: else:
# Use embedded dependencies from virtualenv to ensure determinism # Use embedded dependencies from virtualenv to ensure determinism
venv_args = ["--no-periodic-update", "--pip=embed"] venv_args = ["--no-periodic-update", "--pip=embed"]
@@ -446,10 +466,11 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
log.step(f"Starting container image {build_step.container_image}...") log.step(f"Starting container image {build_step.container_image}...")
print(f"info: This container will host the build for {', '.join(ids_to_build)}...") print(f"info: This container will host the build for {', '.join(ids_to_build)}...")
architecture = Architecture(build_step.platform_tag.split("_", 1)[1])
with OCIContainer( with OCIContainer(
image=build_step.container_image, image=build_step.container_image,
enforce_32_bit=build_step.platform_tag.endswith("i686"), oci_platform=ARCHITECTURE_OCI_PLATFORM_MAP[architecture],
cwd=container_project_path, cwd=container_project_path,
engine=build_step.container_engine, engine=build_step.container_engine,
) as container: ) as container:
+3 -1
View File
@@ -23,11 +23,13 @@ PLATFORM_IDENTIFIER_DESCRIPTIONS: Final[dict[str, str]] = {
"manylinux_aarch64": "manylinux aarch64", "manylinux_aarch64": "manylinux aarch64",
"manylinux_ppc64le": "manylinux ppc64le", "manylinux_ppc64le": "manylinux ppc64le",
"manylinux_s390x": "manylinux s390x", "manylinux_s390x": "manylinux s390x",
"manylinux_armv7l": "manylinux armv7l",
"musllinux_x86_64": "musllinux x86_64", "musllinux_x86_64": "musllinux x86_64",
"musllinux_i686": "musllinux i686", "musllinux_i686": "musllinux i686",
"musllinux_aarch64": "musllinux aarch64", "musllinux_aarch64": "musllinux aarch64",
"musllinux_ppc64le": "musllinux ppc64le", "musllinux_ppc64le": "musllinux ppc64le",
"musllinux_s390x": "manylinux s390x", "musllinux_s390x": "musllinux s390x",
"musllinux_armv7l": "musllinux armv7l",
"win32": "Windows 32bit", "win32": "Windows 32bit",
"win_amd64": "Windows 64bit", "win_amd64": "Windows 64bit",
"win_arm64": "Windows on ARM 64bit", "win_arm64": "Windows on ARM 64bit",
+36 -27
View File
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import functools import functools
import inspect
import os import os
import platform import platform
import re import re
@@ -149,11 +150,15 @@ def install_cpython(tmp: Path, version: str, url: str, free_threading: bool) ->
if detect_ci_provider() is None: if detect_ci_provider() is None:
# if running locally, we don't want to install CPython with sudo # if running locally, we don't want to install CPython with sudo
# let the user know & provide a link to the installer # let the user know & provide a link to the installer
msg = ( msg = inspect.cleandoc(
f"Error: CPython {version} is not installed.\n" f"""
"cibuildwheel will not perform system-wide installs when running outside of CI.\n" Error: CPython {version} is not installed.
f"To build locally, install CPython {version} on this machine, or, disable this version of Python using CIBW_SKIP=cp{version.replace('.', '')}-macosx_*\n" cibuildwheel will not perform system-wide installs when running outside of CI.
f"\nDownload link: {url}" To build locally, install CPython {version} on this machine, or, disable this
version of Python using CIBW_SKIP=cp{version.replace('.', '')}-macosx_*
For portable builds, cibuildwheel needs the official builds from python.org.
Download link: {url}
"""
) )
raise errors.FatalError(msg) raise errors.FatalError(msg)
pkg_path = tmp / "Python.pkg" pkg_path = tmp / "Python.pkg"
@@ -193,6 +198,11 @@ def install_pypy(tmp: Path, url: str) -> Path:
return installation_path / "bin" / "pypy3" return installation_path / "bin" / "pypy3"
def can_use_uv(python_configuration: PythonConfiguration) -> bool:
conditions = (Version(python_configuration.version) >= Version("3.8"),)
return all(conditions)
def setup_python( def setup_python(
tmp: Path, tmp: Path,
python_configuration: PythonConfiguration, python_configuration: PythonConfiguration,
@@ -200,13 +210,11 @@ def setup_python(
environment: ParsedEnvironment, environment: ParsedEnvironment,
build_frontend: BuildFrontendName, build_frontend: BuildFrontendName,
) -> tuple[Path, dict[str, str]]: ) -> tuple[Path, dict[str, str]]:
if build_frontend == "build[uv]" and Version(python_configuration.version) < Version("3.8"): if build_frontend == "build[uv]" and not can_use_uv(python_configuration):
build_frontend = "build" build_frontend = "build"
uv_path = find_uv() uv_path = find_uv()
use_uv = build_frontend == "build[uv]" and Version(python_configuration.version) >= Version( use_uv = build_frontend == "build[uv]"
"3.8"
)
tmp.mkdir() tmp.mkdir()
implementation_id = python_configuration.identifier.split("-")[0] implementation_id = python_configuration.identifier.split("-")[0]
@@ -270,23 +278,23 @@ def setup_python(
# Apply our environment after pip is ready # Apply our environment after pip is ready
env = environment.as_dictionary(prev_environment=env) env = environment.as_dictionary(prev_environment=env)
# check what pip version we're on
if not use_uv:
assert (venv_bin_path / "pip").exists()
call("which", "pip", env=env)
call("pip", "--version", env=env)
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
if which_pip != str(venv_bin_path / "pip"):
msg = "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
raise errors.FatalError(msg)
# check what Python version we're on # check what Python version we're on
call("which", "python", env=env)
call("python", "--version", env=env)
which_python = call("which", "python", env=env, capture_stdout=True).strip() which_python = call("which", "python", env=env, capture_stdout=True).strip()
print(which_python)
if which_python != str(venv_bin_path / "python"): if which_python != str(venv_bin_path / "python"):
msg = "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it." msg = "cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
raise errors.FatalError(msg) raise errors.FatalError(msg)
call("python", "--version", env=env)
# check what pip version we're on
if not use_uv:
assert (venv_bin_path / "pip").exists()
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
print(which_pip)
if which_pip != str(venv_bin_path / "pip"):
msg = "cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
raise errors.FatalError(msg)
call("pip", "--version", env=env)
config_is_arm64 = python_configuration.identifier.endswith("arm64") config_is_arm64 = python_configuration.identifier.endswith("arm64")
config_is_universal2 = python_configuration.identifier.endswith("universal2") config_is_universal2 = python_configuration.identifier.endswith("universal2")
@@ -294,10 +302,10 @@ def setup_python(
# Set MACOSX_DEPLOYMENT_TARGET, if the user didn't set it. # Set MACOSX_DEPLOYMENT_TARGET, if the user didn't set it.
# For arm64, the minimal deployment target is 11.0. # For arm64, the minimal deployment target is 11.0.
# On x86_64 (or universal2), use 10.9 as a default. # On x86_64 (or universal2), use 10.9 as a default.
# CPython 3.13 needs 10.13. # CPython 3.12.6+ needs 10.13.
if config_is_arm64: if config_is_arm64:
default_target = "11.0" default_target = "11.0"
elif Version(python_configuration.version) >= Version("3.13"): elif Version(python_configuration.version) >= Version("3.12"):
default_target = "10.13" default_target = "10.13"
elif python_configuration.identifier.startswith("pp") and Version( elif python_configuration.identifier.startswith("pp") and Version(
python_configuration.version python_configuration.version
@@ -415,9 +423,7 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations: for config in python_configurations:
build_options = options.build_options(config.identifier) build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip") build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
use_uv = build_frontend.name == "build[uv]" and Version(config.version) >= Version( use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
"3.8"
)
uv_path = find_uv() uv_path = find_uv()
if use_uv and uv_path is None: if use_uv and uv_path is None:
msg = "uv not found" msg = "uv not found"
@@ -545,7 +551,10 @@ def build(options: Options, tmp_path: Path) -> None:
else: else:
shutil.move(str(built_wheel), repaired_wheel_dir) shutil.move(str(built_wheel), repaired_wheel_dir)
repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) try:
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
except StopIteration:
raise errors.RepairStepProducedNoWheelError() from None
if repaired_wheel.name in {wheel.name for wheel in built_wheels}: if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name) raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
+132 -41
View File
@@ -8,18 +8,23 @@ import shlex
import shutil import shutil
import subprocess import subprocess
import sys import sys
import textwrap
import typing import typing
import uuid import uuid
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path, PurePath, PurePosixPath from pathlib import Path, PurePath, PurePosixPath
from types import TracebackType from types import TracebackType
from typing import IO, Dict, Literal from typing import IO, Dict, Literal
from ._compat.typing import Self from ._compat.typing import Self, assert_never
from .errors import OCIEngineTooOldError
from .logger import log
from .typing import PathOrStr, PopenBytes from .typing import PathOrStr, PopenBytes
from .util import ( from .util import (
CIProvider, CIProvider,
FlexibleVersion,
call, call,
detect_ci_provider, detect_ci_provider,
parse_key_value_string, parse_key_value_string,
@@ -29,6 +34,16 @@ from .util import (
ContainerEngineName = Literal["docker", "podman"] ContainerEngineName = Literal["docker", "podman"]
# Order of the enum matters for tests. 386 shall appear before amd64.
class OCIPlatform(Enum):
i386 = "linux/386"
AMD64 = "linux/amd64"
ARMV7 = "linux/arm/v7"
ARM64 = "linux/arm64"
PPC64LE = "linux/ppc64le"
S390X = "linux/s390x"
@dataclass(frozen=True) @dataclass(frozen=True)
class OCIContainerEngineConfig: class OCIContainerEngineConfig:
name: ContainerEngineName name: ContainerEngineName
@@ -56,6 +71,15 @@ class OCIContainerEngineConfig:
disable_host_mount = ( disable_host_mount = (
strtobool(disable_host_mount_options[-1]) if disable_host_mount_options else False strtobool(disable_host_mount_options[-1]) if disable_host_mount_options else False
) )
if "--platform" in create_args or any(arg.startswith("--platform=") for arg in create_args):
msg = "Using '--platform' in 'container-engine::create_args' is deprecated. It will be ignored."
log.warning(msg)
if "--platform" in create_args:
index = create_args.index("--platform")
create_args.pop(index)
create_args.pop(index)
else:
create_args = [arg for arg in create_args if not arg.startswith("--platform=")]
return OCIContainerEngineConfig( return OCIContainerEngineConfig(
name=name, create_args=tuple(create_args), disable_host_mount=disable_host_mount name=name, create_args=tuple(create_args), disable_host_mount=disable_host_mount
@@ -75,6 +99,52 @@ class OCIContainerEngineConfig:
DEFAULT_ENGINE = OCIContainerEngineConfig("docker") DEFAULT_ENGINE = OCIContainerEngineConfig("docker")
def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
try:
version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True)
version_info = json.loads(version_string.strip())
if engine.name == "docker":
client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
# --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
version = min(client_api_version, server_api_version)
minimum_version = FlexibleVersion("1.41")
minimum_version_str = "20.10.0" # docker version
error_msg = textwrap.dedent(
f"""
Build failed because {engine.name} is too old.
cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}.
The API version found by cibuildwheel is {version}.
"""
)
elif engine.name == "podman":
# podman uses the same version string for "Version" & "ApiVersion"
client_version = FlexibleVersion(version_info["Client"]["Version"])
if "Server" in version_info:
server_version = FlexibleVersion(version_info["Server"]["Version"])
else:
server_version = client_version
# --platform support was introduced in v3
version = min(client_version, server_version)
minimum_version = FlexibleVersion("3")
error_msg = textwrap.dedent(
f"""
Build failed because {engine.name} is too old.
cibuildwheel requires {engine.name}>={minimum_version}.
The version found by cibuildwheel is {version}.
"""
)
else:
assert_never(engine.name)
if version < minimum_version:
raise OCIEngineTooOldError(error_msg) from None
except (subprocess.CalledProcessError, KeyError, ValueError) as e:
msg = f"Build failed because {engine.name} is too old or is not working properly."
raise OCIEngineTooOldError(msg) from e
class OCIContainer: class OCIContainer:
""" """
An object that represents a running OCI (e.g. Docker) container. An object that represents a running OCI (e.g. Docker) container.
@@ -98,7 +168,7 @@ class OCIContainer:
... print(self.debug_info()) ... print(self.debug_info())
""" """
UTILITY_PYTHON = "/opt/python/cp38-cp38/bin/python" UTILITY_PYTHON = "/opt/python/cp39-cp39/bin/python"
process: PopenBytes process: PopenBytes
bash_stdin: IO[bytes] bash_stdin: IO[bytes]
@@ -108,7 +178,7 @@ class OCIContainer:
self, self,
*, *,
image: str, image: str,
enforce_32_bit: bool = False, oci_platform: OCIPlatform,
cwd: PathOrStr | None = None, cwd: PathOrStr | None = None,
engine: OCIContainerEngineConfig = DEFAULT_ENGINE, engine: OCIContainerEngineConfig = DEFAULT_ENGINE,
): ):
@@ -117,14 +187,48 @@ class OCIContainer:
raise ValueError(msg) raise ValueError(msg)
self.image = image self.image = image
self.enforce_32_bit = enforce_32_bit self.oci_platform = oci_platform
self.cwd = cwd self.cwd = cwd
self.name: str | None = None self.name: str | None = None
self.engine = engine self.engine = engine
self.host_tar_format = ""
if sys.platform.startswith("darwin"):
self.host_tar_format = "--format gnutar"
def _get_platform_args(self, *, oci_platform: OCIPlatform | None = None) -> tuple[str, str]:
if oci_platform is None:
oci_platform = self.oci_platform
# we need '--pull=always' otherwise some images with the wrong platform get re-used (e.g. 386 image for amd64)
# c.f. https://github.com/moby/moby/issues/48197#issuecomment-2282802313
pull = "always"
try:
image_platform = call(
self.engine.name,
"image",
"inspect",
self.image,
"--format",
(
"{{.Os}}/{{.Architecture}}/{{.Variant}}"
if len(oci_platform.value.split("/")) == 3
else "{{.Os}}/{{.Architecture}}"
),
capture_stdout=True,
).strip()
if image_platform == oci_platform.value:
# in case the correct image is already present, don't pull
# this allows to run local only images
pull = "never"
except subprocess.CalledProcessError:
pass
return f"--platform={oci_platform.value}", f"--pull={pull}"
def __enter__(self) -> Self: def __enter__(self) -> Self:
self.name = f"cibuildwheel-{uuid.uuid4()}" self.name = f"cibuildwheel-{uuid.uuid4()}"
_check_engine_version(self.engine)
# work-around for Travis-CI PPC64le Docker runs since 2021: # work-around for Travis-CI PPC64le Docker runs since 2021:
# this avoids network splits # this avoids network splits
# https://github.com/pypa/cibuildwheel/issues/904 # https://github.com/pypa/cibuildwheel/issues/904
@@ -133,15 +237,30 @@ class OCIContainer:
if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le": if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le":
network_args = ["--network=host"] network_args = ["--network=host"]
platform_args = self._get_platform_args()
simulate_32_bit = False simulate_32_bit = False
if self.enforce_32_bit: if self.oci_platform in {OCIPlatform.i386, OCIPlatform.ARMV7}:
# If the architecture running the image is already the right one # If the architecture running the image is already the right one
# or the image entrypoint takes care of enforcing this, then we don't need to # or the image entrypoint takes care of enforcing this, then we don't need to
# simulate this # simulate this
container_machine = call( run_cmd = [self.engine.name, "run", "--rm"]
self.engine.name, "run", "--rm", self.image, "uname", "-m", capture_stdout=True ctr_cmd = ["uname", "-m"]
).strip() try:
simulate_32_bit = container_machine != "i686" container_machine = call(
*run_cmd, *platform_args, self.image, *ctr_cmd, capture_stdout=True
).strip()
except subprocess.CalledProcessError:
if self.oci_platform == OCIPlatform.i386:
# The image might have been built with amd64 architecture
# Let's try that
platform_args = self._get_platform_args(oci_platform=OCIPlatform.AMD64)
container_machine = call(
*run_cmd, *platform_args, self.image, *ctr_cmd, capture_stdout=True
).strip()
else:
raise
simulate_32_bit = container_machine not in {"i686", "armv7l", "armv8l"}
shell_args = ["linux32", "/bin/bash"] if simulate_32_bit else ["/bin/bash"] shell_args = ["linux32", "/bin/bash"] if simulate_32_bit else ["/bin/bash"]
@@ -155,6 +274,7 @@ class OCIContainer:
"--interactive", "--interactive",
*(["--volume=/:/host"] if not self.engine.disable_host_mount else []), *(["--volume=/:/host"] if not self.engine.disable_host_mount else []),
*network_args, *network_args,
*platform_args,
*self.engine.create_args, *self.engine.create_args,
self.image, self.image,
*shell_args, *shell_args,
@@ -221,15 +341,10 @@ class OCIContainer:
self.name = None self.name = None
def copy_into(self, from_path: Path, to_path: PurePath) -> None: def copy_into(self, from_path: Path, to_path: PurePath) -> None:
# `docker cp` causes 'no space left on device' error when
# a container is running and the host filesystem is
# mounted. https://github.com/moby/moby/issues/38995
# Use `docker exec` instead.
if from_path.is_dir(): if from_path.is_dir():
self.call(["mkdir", "-p", to_path]) self.call(["mkdir", "-p", to_path])
subprocess.run( subprocess.run(
f"tar cf - . | {self.engine.name} exec -i {self.name} tar --no-same-owner -xC {shell_quote(to_path)} -f -", f"tar -c {self.host_tar_format} -f - . | {self.engine.name} exec -i {self.name} tar --no-same-owner -xC {shell_quote(to_path)} -f -",
shell=True, shell=True,
check=True, check=True,
cwd=from_path, cwd=from_path,
@@ -250,8 +365,7 @@ class OCIContainer:
) as exec_process: ) as exec_process:
assert exec_process.stdin assert exec_process.stdin
with open(from_path, "rb") as from_file: with open(from_path, "rb") as from_file:
# Bug in mypy, https://github.com/python/mypy/issues/15031 shutil.copyfileobj(from_file, exec_process.stdin)
shutil.copyfileobj(from_file, exec_process.stdin) # type: ignore[misc]
exec_process.stdin.close() exec_process.stdin.close()
exec_process.wait() exec_process.wait()
@@ -264,30 +378,7 @@ class OCIContainer:
def copy_out(self, from_path: PurePath, to_path: Path) -> None: def copy_out(self, from_path: PurePath, to_path: Path) -> None:
# note: we assume from_path is a dir # note: we assume from_path is a dir
to_path.mkdir(parents=True, exist_ok=True) to_path.mkdir(parents=True, exist_ok=True)
call(self.engine.name, "cp", f"{self.name}:{from_path}/.", to_path)
if self.engine.name == "podman":
subprocess.run(
[
self.engine.name,
"cp",
f"{self.name}:{from_path}/.",
str(to_path),
],
check=True,
cwd=to_path,
)
elif self.engine.name == "docker":
# There is a bug in docker that prevents a simple 'cp' invocation
# from working https://github.com/moby/moby/issues/38995
command = f"{self.engine.name} exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -"
subprocess.run(
command,
shell=True,
check=True,
cwd=to_path,
)
else:
raise KeyError(self.engine.name)
def glob(self, path: PurePosixPath, pattern: str) -> list[PurePosixPath]: def glob(self, path: PurePosixPath, pattern: str) -> list[PurePosixPath]:
glob_pattern = path.joinpath(pattern) glob_pattern = path.joinpath(pattern)
+86 -21
View File
@@ -22,7 +22,7 @@ from .architecture import Architecture
from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment
from .logger import log from .logger import log
from .oci_container import OCIContainerEngineConfig from .oci_container import OCIContainerEngineConfig
from .projectfiles import get_requires_python_str from .projectfiles import get_requires_python_str, resolve_dependency_groups
from .typing import PLATFORMS, PlatformName from .typing import PLATFORMS, PlatformName
from .util import ( from .util import (
MANYLINUX_ARCHS, MANYLINUX_ARCHS,
@@ -30,8 +30,10 @@ from .util import (
BuildFrontendConfig, BuildFrontendConfig,
BuildSelector, BuildSelector,
DependencyConstraints, DependencyConstraints,
EnableGroups,
TestSelector, TestSelector,
format_safe, format_safe,
read_python_configs,
resources_dir, resources_dir,
selector_matches, selector_matches,
strtobool, strtobool,
@@ -92,6 +94,7 @@ class BuildOptions:
before_test: str | None before_test: str | None
test_requires: list[str] test_requires: list[str]
test_extras: str test_extras: str
test_groups: list[str]
build_verbosity: int build_verbosity: int
build_frontend: BuildFrontendConfig | None build_frontend: BuildFrontendConfig | None
config_settings: str config_settings: str
@@ -307,7 +310,6 @@ def _resolve_cascade(
return result return result
# pylint: disable-next=inconsistent-return-statements
def _apply_inherit_rule( def _apply_inherit_rule(
before: str | None, after: str, rule: InheritRule, option_format: OptionFormat | None before: str | None, after: str, rule: InheritRule, option_format: OptionFormat | None
) -> str: ) -> str:
@@ -329,10 +331,10 @@ def _apply_inherit_rule(
if rule == InheritRule.APPEND: if rule == InheritRule.APPEND:
return option_format.merge_values(before, after) return option_format.merge_values(before, after)
elif rule == InheritRule.PREPEND: if rule == InheritRule.PREPEND:
return option_format.merge_values(after, before) return option_format.merge_values(after, before)
else:
assert_never(rule) assert_never(rule)
def _stringify_setting( def _stringify_setting(
@@ -512,6 +514,7 @@ class OptionsReader:
env_plat: bool = True, env_plat: bool = True,
option_format: OptionFormat | None = None, option_format: OptionFormat | None = None,
ignore_empty: bool = False, ignore_empty: bool = False,
env_rule: InheritRule = InheritRule.NONE,
) -> str: ) -> str:
""" """
Get and return the value for the named option from environment, Get and return the value for the named option from environment,
@@ -543,33 +546,44 @@ class OptionsReader:
(o.options.get(name), o.inherit.get(name, InheritRule.NONE)) (o.options.get(name), o.inherit.get(name, InheritRule.NONE))
for o in self.active_config_overrides for o in self.active_config_overrides
], ],
(self.env.get(envvar), InheritRule.NONE), (self.env.get(envvar), env_rule),
(self.env.get(plat_envvar) if env_plat else None, InheritRule.NONE), (self.env.get(plat_envvar) if env_plat else None, env_rule),
ignore_empty=ignore_empty, ignore_empty=ignore_empty,
option_format=option_format, option_format=option_format,
) )
class Options: class Options:
pyproject_toml: dict[str, Any] | None
def __init__( def __init__(
self, self,
platform: PlatformName, platform: PlatformName,
command_line_arguments: CommandLineArguments, command_line_arguments: CommandLineArguments,
env: Mapping[str, str], env: Mapping[str, str],
read_config_file: bool = True, defaults: bool = False,
): ):
self.platform = platform self.platform = platform
self.command_line_arguments = command_line_arguments self.command_line_arguments = command_line_arguments
self.env = env self.env = env
self._defaults = defaults
self._image_warnings: set[str] = set()
self.reader = OptionsReader( self.reader = OptionsReader(
self.config_file_path if read_config_file else None, None if defaults else self.config_file_path,
platform=platform, platform=platform,
env=env, env=env,
disallow=DISALLOWED_OPTIONS, disallow=DISALLOWED_OPTIONS,
) )
@property self.package_dir = Path(command_line_arguments.package_dir)
try:
with self.package_dir.joinpath("pyproject.toml").open("rb") as f:
self.pyproject_toml = tomllib.load(f)
except FileNotFoundError:
self.pyproject_toml = None
@functools.cached_property
def config_file_path(self) -> Path | None: def config_file_path(self) -> Path | None:
args = self.command_line_arguments args = self.command_line_arguments
@@ -585,10 +599,9 @@ class Options:
@functools.cached_property @functools.cached_property
def package_requires_python_str(self) -> str | None: def package_requires_python_str(self) -> str | None:
args = self.command_line_arguments return get_requires_python_str(self.package_dir, self.pyproject_toml)
return get_requires_python_str(Path(args.package_dir))
@property @functools.cached_property
def globals(self) -> GlobalOptions: def globals(self) -> GlobalOptions:
args = self.command_line_arguments args = self.command_line_arguments
package_dir = args.package_dir package_dir = args.package_dir
@@ -600,16 +613,34 @@ class Options:
skip_config = self.reader.get("skip", env_plat=False, option_format=ListFormat(sep=" ")) skip_config = self.reader.get("skip", env_plat=False, option_format=ListFormat(sep=" "))
test_skip = self.reader.get("test-skip", env_plat=False, option_format=ListFormat(sep=" ")) test_skip = self.reader.get("test-skip", env_plat=False, option_format=ListFormat(sep=" "))
allow_empty = args.allow_empty or strtobool(self.env.get("CIBW_ALLOW_EMPTY", "0"))
enable_groups = self.reader.get(
"enable", env_plat=False, option_format=ListFormat(sep=" "), env_rule=InheritRule.APPEND
)
enable = {EnableGroups(group) for group in enable_groups.split()}
free_threaded_support = strtobool( free_threaded_support = strtobool(
self.reader.get("free-threaded-support", env_plat=False, ignore_empty=True) self.reader.get("free-threaded-support", env_plat=False, ignore_empty=True)
) )
allow_empty = args.allow_empty or strtobool(self.env.get("CIBW_ALLOW_EMPTY", "0"))
prerelease_pythons = args.prerelease_pythons or strtobool( prerelease_pythons = args.prerelease_pythons or strtobool(
self.env.get("CIBW_PRERELEASE_PYTHONS", "0") self.env.get("CIBW_PRERELEASE_PYTHONS", "0")
) )
if free_threaded_support or prerelease_pythons:
msg = (
"free-threaded-support and prerelease-pythons should be specified by enable instead"
)
if enable:
raise OptionsReaderError(msg)
log.warning(msg)
if free_threaded_support:
enable.add(EnableGroups.CPythonFreeThreading)
if prerelease_pythons:
enable.add(EnableGroups.CPythonPrerelease)
# This is not supported in tool.cibuildwheel, as it comes from a standard location. # This is not supported in tool.cibuildwheel, as it comes from a standard location.
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py # Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
requires_python_str: str | None = ( requires_python_str: str | None = (
@@ -625,18 +656,30 @@ class Options:
build_config = args.only build_config = args.only
skip_config = "" skip_config = ""
architectures = Architecture.all_archs(self.platform) architectures = Architecture.all_archs(self.platform)
prerelease_pythons = True enable = set(EnableGroups)
free_threaded_support = True
build_selector = BuildSelector( build_selector = BuildSelector(
build_config=build_config, build_config=build_config,
skip_config=skip_config, skip_config=skip_config,
requires_python=requires_python, requires_python=requires_python,
prerelease_pythons=prerelease_pythons, enable=frozenset(
free_threaded_support=free_threaded_support, enable | {EnableGroups.PyPy}
), # For backwards compatibility, we are adding PyPy for now
) )
test_selector = TestSelector(skip_config=test_skip) test_selector = TestSelector(skip_config=test_skip)
all_configs = read_python_configs(self.platform)
all_pypy_ids = {
config["identifier"] for config in all_configs if config["identifier"].startswith("pp")
}
if (
not self._defaults
and EnableGroups.PyPy not in enable
and any(build_selector(build_id) for build_id in all_pypy_ids)
):
msg = "PyPy builds will be disabled by default in version 3. Enabling PyPy builds should be specified by enable"
log.warning(msg)
return GlobalOptions( return GlobalOptions(
package_dir=package_dir, package_dir=package_dir,
output_dir=output_dir, output_dir=output_dir,
@@ -646,6 +689,20 @@ class Options:
allow_empty=allow_empty, allow_empty=allow_empty,
) )
def _check_pinned_image(self, value: str, pinned_images: Mapping[str, str]) -> None:
if (
value in {"manylinux1", "manylinux2010", "manylinux_2_24", "musllinux_1_1"}
and value not in self._image_warnings
):
self._image_warnings.add(value)
msg = (
f"Deprecated image {value!r}. This value will not work"
" in a future version of cibuildwheel. Either upgrade to a supported"
" image or continue using the deprecated image by pinning directly"
f" to {pinned_images[value]!r}."
)
log.warning(msg)
def build_options(self, identifier: str | None) -> BuildOptions: def build_options(self, identifier: str | None) -> BuildOptions:
""" """
Compute BuildOptions for a single run configuration. Compute BuildOptions for a single run configuration.
@@ -673,6 +730,11 @@ class Options:
"test-requires", option_format=ListFormat(sep=" ") "test-requires", option_format=ListFormat(sep=" ")
).split() ).split()
test_extras = self.reader.get("test-extras", option_format=ListFormat(sep=",")) test_extras = self.reader.get("test-extras", option_format=ListFormat(sep=","))
test_groups_str = self.reader.get("test-groups", option_format=ListFormat(sep=" "))
test_groups = [x for x in test_groups_str.split() if x]
test_requirements_from_groups = resolve_dependency_groups(
self.pyproject_toml, *test_groups
)
build_verbosity_str = self.reader.get("build-verbosity") build_verbosity_str = self.reader.get("build-verbosity")
build_frontend_str = self.reader.get( build_frontend_str = self.reader.get(
@@ -738,6 +800,7 @@ class Options:
# default to manylinux2014 # default to manylinux2014
image = pinned_images["manylinux2014"] image = pinned_images["manylinux2014"]
elif config_value in pinned_images: elif config_value in pinned_images:
self._check_pinned_image(config_value, pinned_images)
image = pinned_images[config_value] image = pinned_images[config_value]
else: else:
image = config_value image = config_value
@@ -752,6 +815,7 @@ class Options:
if not config_value: if not config_value:
image = pinned_images["musllinux_1_2"] image = pinned_images["musllinux_1_2"]
elif config_value in pinned_images: elif config_value in pinned_images:
self._check_pinned_image(config_value, pinned_images)
image = pinned_images[config_value] image = pinned_images[config_value]
else: else:
image = config_value image = config_value
@@ -772,8 +836,9 @@ class Options:
return BuildOptions( return BuildOptions(
globals=self.globals, globals=self.globals,
test_command=test_command, test_command=test_command,
test_requires=test_requires, test_requires=[*test_requires, *test_requirements_from_groups],
test_extras=test_extras, test_extras=test_extras,
test_groups=test_groups,
before_test=before_test, before_test=before_test,
before_build=before_build, before_build=before_build,
before_all=before_all, before_all=before_all,
@@ -817,7 +882,7 @@ class Options:
platform=self.platform, platform=self.platform,
command_line_arguments=CommandLineArguments.defaults(), command_line_arguments=CommandLineArguments.defaults(),
env={}, env={},
read_config_file=False, defaults=True,
) )
def summary(self, identifiers: Iterable[str]) -> str: def summary(self, identifiers: Iterable[str]) -> str:
+28 -7
View File
@@ -4,8 +4,9 @@ import ast
import configparser import configparser
import contextlib import contextlib
from pathlib import Path from pathlib import Path
from typing import Any
from ._compat import tomllib import dependency_groups
def get_parent(node: ast.AST | None, depth: int = 1) -> ast.AST | None: def get_parent(node: ast.AST | None, depth: int = 1) -> ast.AST | None:
@@ -84,15 +85,12 @@ def setup_py_python_requires(content: str) -> str | None:
return None return None
def get_requires_python_str(package_dir: Path) -> str | None: def get_requires_python_str(package_dir: Path, pyproject_toml: dict[str, Any] | None) -> str | None:
"""Return the python requires string from the most canonical source available, or None""" """Return the python requires string from the most canonical source available, or None"""
# Read in from pyproject.toml:project.requires-python # Read in from pyproject.toml:project.requires-python
with contextlib.suppress(FileNotFoundError): with contextlib.suppress(KeyError, IndexError, TypeError):
with (package_dir / "pyproject.toml").open("rb") as f1: return str((pyproject_toml or {})["project"]["requires-python"])
info = tomllib.load(f1)
with contextlib.suppress(KeyError, IndexError, TypeError):
return str(info["project"]["requires-python"])
# Read in from setup.cfg:options.python_requires # Read in from setup.cfg:options.python_requires
config = configparser.ConfigParser() config = configparser.ConfigParser()
@@ -106,3 +104,26 @@ def get_requires_python_str(package_dir: Path) -> str | None:
return setup_py_python_requires(f2.read()) return setup_py_python_requires(f2.read())
return None return None
def resolve_dependency_groups(
pyproject_toml: dict[str, Any] | None, *groups: str
) -> tuple[str, ...]:
"""
Get the packages in dependency-groups for a package.
"""
if not groups:
return ()
if pyproject_toml is None:
msg = f"Didn't find a pyproject.toml, so can't read [dependency-groups] {groups!r} from it!"
raise FileNotFoundError(msg)
try:
dependency_groups_toml = pyproject_toml["dependency-groups"]
except KeyError:
msg = f"Didn't find [dependency-groups] in pyproject.toml, which is needed to resolve {groups!r}."
raise KeyError(msg) from None
return dependency_groups.resolve(dependency_groups_toml, *groups)
+24 -17
View File
@@ -41,6 +41,7 @@ class PythonConfiguration:
version: str version: str
identifier: str identifier: str
pyodide_version: str pyodide_version: str
pyodide_build_version: str
emscripten_version: str emscripten_version: str
node_version: str node_version: str
@@ -65,10 +66,15 @@ def install_emscripten(tmp: Path, version: str) -> Path:
return emcc_path return emcc_path
def install_xbuildenv(env: dict[str, str], pyodide_version: str) -> str: def install_xbuildenv(env: dict[str, str], pyodide_build_version: str, pyodide_version: str) -> str:
"""Install a particular Pyodide xbuildenv version and set a path to the Pyodide root."""
# Since pyodide-build was unvendored from Pyodide v0.27.0, the versions of pyodide-build are
# not guaranteed to match the versions of Pyodide or be in sync with them. Hence, we shall
# specify the pyodide-build version in the root path, which will set up the xbuildenv for
# the requested Pyodide version.
pyodide_root = ( pyodide_root = (
CIBW_CACHE_PATH CIBW_CACHE_PATH
/ f".pyodide-xbuildenv-{pyodide_version}/{pyodide_version}/xbuildenv/pyodide-root" / f".pyodide-xbuildenv-{pyodide_build_version}/{pyodide_version}/xbuildenv/pyodide-root"
) )
with FileLock(CIBW_CACHE_PATH / "xbuildenv.lock"): with FileLock(CIBW_CACHE_PATH / "xbuildenv.lock"):
if pyodide_root.exists(): if pyodide_root.exists():
@@ -136,22 +142,22 @@ def setup_python(
env = environment.as_dictionary(prev_environment=env) env = environment.as_dictionary(prev_environment=env)
# check what pip version we're on
assert (venv_bin_path / "pip").exists()
call("which", "pip", env=env)
call("pip", "--version", env=env)
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
if which_pip != str(venv_bin_path / "pip"):
msg = "pip available on PATH doesn't match our venv instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
raise errors.FatalError(msg)
# check what Python version we're on # check what Python version we're on
call("which", "python", env=env)
call("python", "--version", env=env)
which_python = call("which", "python", env=env, capture_stdout=True).strip() which_python = call("which", "python", env=env, capture_stdout=True).strip()
print(which_python)
if which_python != str(venv_bin_path / "python"): if which_python != str(venv_bin_path / "python"):
msg = "python available on PATH doesn't match our venv instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it." msg = "python available on PATH doesn't match our venv instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
raise errors.FatalError(msg) raise errors.FatalError(msg)
call("python", "--version", env=env)
# check what pip version we're on
assert (venv_bin_path / "pip").exists()
which_pip = call("which", "pip", env=env, capture_stdout=True).strip()
print(which_pip)
if which_pip != str(venv_bin_path / "pip"):
msg = "pip available on PATH doesn't match our venv instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
raise errors.FatalError(msg)
call("pip", "--version", env=env)
log.step("Installing build tools...") log.step("Installing build tools...")
call( call(
@@ -159,19 +165,20 @@ def setup_python(
"install", "install",
"--upgrade", "--upgrade",
"auditwheel-emscripten", "auditwheel-emscripten",
"build[virtualenv]",
"pyodide-build", "pyodide-build",
*dependency_constraint_flags, *dependency_constraint_flags,
env=env, env=env,
) )
log.step("Installing emscripten...") log.step(f"Installing Emscripten version: {python_configuration.emscripten_version} ...")
emcc_path = install_emscripten(tmp, python_configuration.emscripten_version) emcc_path = install_emscripten(tmp, python_configuration.emscripten_version)
env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]]) env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]])
log.step("Installing Pyodide xbuildenv...") log.step(f"Installing Pyodide xbuildenv version: {python_configuration.pyodide_version} ...")
env["PYODIDE_ROOT"] = install_xbuildenv(env, python_configuration.pyodide_version) env["PYODIDE_ROOT"] = install_xbuildenv(
env, python_configuration.pyodide_build_version, python_configuration.pyodide_version
)
return env return env
+46 -22
View File
@@ -22,6 +22,7 @@ python_configurations = [
{ identifier = "pp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" }, { identifier = "pp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" }, { identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
{ identifier = "pp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" }, { identifier = "pp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
{ identifier = "pp311-manylinux_x86_64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
{ identifier = "cp36-manylinux_aarch64", version = "3.6", path_str = "/opt/python/cp36-cp36m" }, { identifier = "cp36-manylinux_aarch64", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/cp37-cp37m" }, { identifier = "cp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" }, { identifier = "cp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
@@ -49,14 +50,25 @@ python_configurations = [
{ identifier = "cp312-manylinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" }, { identifier = "cp312-manylinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313" }, { identifier = "cp313-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" }, { identifier = "cp313t-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-manylinux_armv7l", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_armv7l", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_armv7l", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_armv7l", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_armv7l", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_armv7l", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp312-manylinux_armv7l", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "pp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" }, { identifier = "pp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" }, { identifier = "pp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" }, { identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
{ identifier = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" }, { identifier = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
{ identifier = "pp311-manylinux_aarch64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
{ identifier = "pp37-manylinux_i686", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" }, { identifier = "pp37-manylinux_i686", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp38-manylinux_i686", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" }, { identifier = "pp38-manylinux_i686", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_i686", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" }, { identifier = "pp39-manylinux_i686", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
{ identifier = "pp310-manylinux_i686", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" }, { identifier = "pp310-manylinux_i686", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
{ identifier = "pp311-manylinux_i686", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
{ identifier = "cp36-musllinux_x86_64", version = "3.6", path_str = "/opt/python/cp36-cp36m" }, { identifier = "cp36-musllinux_x86_64", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_x86_64", version = "3.7", path_str = "/opt/python/cp37-cp37m" }, { identifier = "cp37-musllinux_x86_64", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_x86_64", version = "3.8", path_str = "/opt/python/cp38-cp38" }, { identifier = "cp38-musllinux_x86_64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
@@ -102,6 +114,15 @@ python_configurations = [
{ identifier = "cp312-musllinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" }, { identifier = "cp312-musllinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313" }, { identifier = "cp313-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" }, { identifier = "cp313t-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-musllinux_armv7l", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_armv7l", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_armv7l", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_armv7l", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_armv7l", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_armv7l", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp312-musllinux_armv7l", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_armv7l", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
] ]
[macos] [macos]
@@ -120,22 +141,24 @@ python_configurations = [
{ identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" }, { identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
{ identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" }, { identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
{ identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" }, { identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.5/python-3.12.5-macos11.pkg" }, { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.5/python-3.12.5-macos11.pkg" }, { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.5/python-3.12.5-macos11.pkg" }, { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0rc1-macos11.pkg" }, { identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0rc1-macos11.pkg" }, { identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0rc1-macos11.pkg" }, { identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
{ identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0rc1-macos11.pkg" }, { identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
{ identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0rc1-macos11.pkg" }, { identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
{ identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0rc1-macos11.pkg" }, { identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-macos11.pkg" },
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" }, { identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
{ identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" }, { identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
{ identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" }, { identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
{ identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" }, { identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
{ identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2" }, { identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2" },
{ identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.17-macos_x86_64.tar.bz2" }, { identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_x86_64.tar.bz2" },
{ identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.17-macos_arm64.tar.bz2" }, { identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
{ identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_x86_64.tar.bz2" },
{ identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-macos_arm64.tar.bz2" },
] ]
[windows] [windows]
@@ -152,25 +175,26 @@ python_configurations = [
{ identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" }, { identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
{ identifier = "cp311-win32", version = "3.11.9", arch = "32" }, { identifier = "cp311-win32", version = "3.11.9", arch = "32" },
{ identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" }, { identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
{ identifier = "cp312-win32", version = "3.12.5", arch = "32" }, { identifier = "cp312-win32", version = "3.12.10", arch = "32" },
{ identifier = "cp312-win_amd64", version = "3.12.5", arch = "64" }, { identifier = "cp312-win_amd64", version = "3.12.10", arch = "64" },
{ identifier = "cp313-win32", version = "3.13.0-rc1", arch = "32" }, { identifier = "cp313-win32", version = "3.13.3", arch = "32" },
{ identifier = "cp313t-win32", version = "3.13.0-rc1", arch = "32" }, { identifier = "cp313t-win32", version = "3.13.3", arch = "32" },
{ identifier = "cp313-win_amd64", version = "3.13.0-rc1", arch = "64" }, { identifier = "cp313-win_amd64", version = "3.13.3", arch = "64" },
{ identifier = "cp313t-win_amd64", version = "3.13.0-rc1", arch = "64" }, { identifier = "cp313t-win_amd64", version = "3.13.3", arch = "64" },
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" }, { identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
{ identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" }, { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
{ identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" }, { identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
{ identifier = "cp312-win_arm64", version = "3.12.5", arch = "ARM64" }, { identifier = "cp312-win_arm64", version = "3.12.10", arch = "ARM64" },
{ identifier = "cp313-win_arm64", version = "3.13.0-rc1", arch = "ARM64" }, { identifier = "cp313-win_arm64", version = "3.13.3", arch = "ARM64" },
{ identifier = "cp313t-win_arm64", version = "3.13.0-rc1", arch = "ARM64" }, { identifier = "cp313t-win_arm64", version = "3.13.3", arch = "ARM64" },
{ identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" }, { identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" }, { identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" }, { identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
{ identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.17-win64.zip" }, { identifier = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
{ identifier = "pp311-win_amd64", version = "3.11", arch = "64", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.19-win64.zip" },
] ]
[pyodide] [pyodide]
python_configurations = [ python_configurations = [
{ identifier = "cp312-pyodide_wasm32", version = "3.12.1", pyodide_version = "0.26.1", emscripten_version = "3.1.58", node_version = "v20" }, { identifier = "cp312-pyodide_wasm32", version = "3.12", pyodide_version = "0.27.0", pyodide_build_version = "0.29.2", emscripten_version = "3.1.58", node_version = "v20" },
] ]
@@ -10,7 +10,15 @@
], ],
"default": "none", "default": "none",
"description": "How to inherit the parent's value." "description": "How to inherit the parent's value."
} },
"enable": {
"enum": [
"cpython-freethreading",
"cpython-prerelease",
"pypy"
]
},
"description": "A Python version or flavor to enable."
}, },
"additionalProperties": false, "additionalProperties": false,
"description": "cibuildwheel's settings.", "description": "cibuildwheel's settings.",
@@ -228,6 +236,21 @@
"type": "string", "type": "string",
"title": "CIBW_DEPENDENCY_VERSIONS" "title": "CIBW_DEPENDENCY_VERSIONS"
}, },
"enable": {
"description": "Enable or disable certain builds.",
"oneOf": [
{
"$ref": "#/$defs/enable"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/enable"
}
}
],
"title": "CIBW_ENABLE"
},
"environment": { "environment": {
"description": "Set environment variables needed during the build.", "description": "Set environment variables needed during the build.",
"oneOf": [ "oneOf": [
@@ -265,6 +288,7 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "The project supports free-threaded builds of Python (PEP703)", "description": "The project supports free-threaded builds of Python (PEP703)",
"deprecated": "Use the `enable` option instead.",
"title": "CIBW_FREE_THREADED_SUPPORT" "title": "CIBW_FREE_THREADED_SUPPORT"
}, },
"manylinux-aarch64-image": { "manylinux-aarch64-image": {
@@ -272,6 +296,11 @@
"description": "Specify alternative manylinux / musllinux container images", "description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MANYLINUX_AARCH64_IMAGE" "title": "CIBW_MANYLINUX_AARCH64_IMAGE"
}, },
"manylinux-armv7l-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MANYLINUX_ARMV7L_IMAGE"
},
"manylinux-i686-image": { "manylinux-i686-image": {
"type": "string", "type": "string",
"description": "Specify alternative manylinux / musllinux container images", "description": "Specify alternative manylinux / musllinux container images",
@@ -312,6 +341,11 @@
"description": "Specify alternative manylinux / musllinux container images", "description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MUSLLINUX_AARCH64_IMAGE" "title": "CIBW_MUSLLINUX_AARCH64_IMAGE"
}, },
"musllinux-armv7l-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MUSLLINUX_ARMV7L_IMAGE"
},
"musllinux-i686-image": { "musllinux-i686-image": {
"type": "string", "type": "string",
"description": "Specify alternative manylinux / musllinux container images", "description": "Specify alternative manylinux / musllinux container images",
@@ -392,6 +426,21 @@
], ],
"title": "CIBW_TEST_EXTRAS" "title": "CIBW_TEST_EXTRAS"
}, },
"test-groups": {
"description": "Install extra groups when testing",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"title": "CIBW_TEST_GROUPS"
},
"test-requires": { "test-requires": {
"description": "Install Python dependencies before running the tests", "description": "Install Python dependencies before running the tests",
"oneOf": [ "oneOf": [
@@ -518,6 +567,9 @@
"manylinux-aarch64-image": { "manylinux-aarch64-image": {
"$ref": "#/properties/manylinux-aarch64-image" "$ref": "#/properties/manylinux-aarch64-image"
}, },
"manylinux-armv7l-image": {
"$ref": "#/properties/manylinux-armv7l-image"
},
"manylinux-i686-image": { "manylinux-i686-image": {
"$ref": "#/properties/manylinux-i686-image" "$ref": "#/properties/manylinux-i686-image"
}, },
@@ -542,6 +594,9 @@
"musllinux-aarch64-image": { "musllinux-aarch64-image": {
"$ref": "#/properties/musllinux-aarch64-image" "$ref": "#/properties/musllinux-aarch64-image"
}, },
"musllinux-armv7l-image": {
"$ref": "#/properties/musllinux-armv7l-image"
},
"musllinux-i686-image": { "musllinux-i686-image": {
"$ref": "#/properties/musllinux-i686-image" "$ref": "#/properties/musllinux-i686-image"
}, },
@@ -563,6 +618,9 @@
"test-extras": { "test-extras": {
"$ref": "#/properties/test-extras" "$ref": "#/properties/test-extras"
}, },
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": { "test-requires": {
"$ref": "#/properties/test-requires" "$ref": "#/properties/test-requires"
} }
@@ -606,6 +664,9 @@
"manylinux-aarch64-image": { "manylinux-aarch64-image": {
"$ref": "#/properties/manylinux-aarch64-image" "$ref": "#/properties/manylinux-aarch64-image"
}, },
"manylinux-armv7l-image": {
"$ref": "#/properties/manylinux-armv7l-image"
},
"manylinux-i686-image": { "manylinux-i686-image": {
"$ref": "#/properties/manylinux-i686-image" "$ref": "#/properties/manylinux-i686-image"
}, },
@@ -630,6 +691,9 @@
"musllinux-aarch64-image": { "musllinux-aarch64-image": {
"$ref": "#/properties/musllinux-aarch64-image" "$ref": "#/properties/musllinux-aarch64-image"
}, },
"musllinux-armv7l-image": {
"$ref": "#/properties/musllinux-armv7l-image"
},
"musllinux-i686-image": { "musllinux-i686-image": {
"$ref": "#/properties/musllinux-i686-image" "$ref": "#/properties/musllinux-i686-image"
}, },
@@ -664,6 +728,9 @@
"test-extras": { "test-extras": {
"$ref": "#/properties/test-extras" "$ref": "#/properties/test-extras"
}, },
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": { "test-requires": {
"$ref": "#/properties/test-requires" "$ref": "#/properties/test-requires"
} }
@@ -709,6 +776,9 @@
"test-extras": { "test-extras": {
"$ref": "#/properties/test-extras" "$ref": "#/properties/test-extras"
}, },
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": { "test-requires": {
"$ref": "#/properties/test-requires" "$ref": "#/properties/test-requires"
} }
@@ -767,6 +837,9 @@
"test-extras": { "test-extras": {
"$ref": "#/properties/test-extras" "$ref": "#/properties/test-extras"
}, },
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": { "test-requires": {
"$ref": "#/properties/test-requires" "$ref": "#/properties/test-requires"
} }
@@ -812,6 +885,9 @@
"test-extras": { "test-extras": {
"$ref": "#/properties/test-extras" "$ref": "#/properties/test-extras"
}, },
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": { "test-requires": {
"$ref": "#/properties/test-requires" "$ref": "#/properties/test-requires"
} }
@@ -2,122 +2,114 @@
# nox -s update_constraints # nox -s update_constraints
annotated-types==0.7.0 annotated-types==0.7.0
# via pydantic # via pydantic
anyio==4.4.0 anyio==4.9.0
# via httpx # via httpx
auditwheel-emscripten==0.0.16 auditwheel-emscripten==0.0.16
# via pyodide-build # via pyodide-build
build==1.2.1 build==1.2.2.post1
# via # via
# -r .nox/update_constraints/tmp/constraints-pyodide.in # -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build # pyodide-build
certifi==2024.8.30 certifi==2025.1.31
# via # via
# httpcore # httpcore
# httpx # httpx
# requests # requests
charset-normalizer==3.3.2 charset-normalizer==3.4.1
# via requests # via requests
click==8.1.7 click==8.1.8
# via typer # via typer
cloudpickle==3.0.0 cmake==4.0.0
# via loky
cmake==3.30.2
# via pyodide-build # via pyodide-build
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
h11==0.14.0 h11==0.14.0
# via httpcore # via httpcore
httpcore==1.0.5 httpcore==1.0.8
# via httpx # via httpx
httpx==0.27.2 httpx==0.28.1
# via unearth # via unearth
idna==3.8 idna==3.10
# via # via
# anyio # anyio
# httpx # httpx
# requests # requests
leb128==1.0.8 leb128==1.0.8
# via auditwheel-emscripten # via auditwheel-emscripten
loky==3.4.1
# via pyodide-build
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via rich # via rich
mdurl==0.1.2 mdurl==0.1.2
# via markdown-it-py # via markdown-it-py
packaging==24.1 packaging==25.0
# via # via
# auditwheel-emscripten # auditwheel-emscripten
# build # build
# pyodide-build # pyodide-build
# unearth # unearth
pip==24.2 pip==25.0.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in # via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pydantic==2.8.2 pydantic==2.11.3
# via # via
# pyodide-build # pyodide-build
# pyodide-lock # pyodide-lock
pydantic-core==2.20.1 pydantic-core==2.33.1
# via pydantic # via pydantic
pygments==2.18.0 pygments==2.19.1
# via rich # via rich
pyodide-build==0.26.1 pyodide-build==0.29.2
# via -r .nox/update_constraints/tmp/constraints-pyodide.in # via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.2.4 pyodide-cli==0.2.4
# via # via
# auditwheel-emscripten # auditwheel-emscripten
# pyodide-build # pyodide-build
pyodide-lock==0.1.0a6 pyodide-lock==0.1.0a7
# via pyodide-build # via pyodide-build
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
pyyaml==6.0.2
# via pyodide-build
requests==2.32.3 requests==2.32.3
# via pyodide-build # via pyodide-build
resolvelib==1.0.1 resolvelib==1.1.0
# via pyodide-build # via pyodide-build
rich==13.8.0 rich==14.0.0
# via # via
# pyodide-build # pyodide-build
# pyodide-cli # pyodide-cli
# typer # typer
ruamel-yaml==0.18.6 ruamel-yaml==0.18.10
# via pyodide-build # via pyodide-build
ruamel-yaml-clib==0.2.8 ruamel-yaml-clib==0.2.12
# via ruamel-yaml # via ruamel-yaml
shellingham==1.5.4 shellingham==1.5.4
# via typer # via typer
sniffio==1.3.1 sniffio==1.3.1
# via # via anyio
# anyio typer==0.15.2
# httpx
typer==0.12.5
# via # via
# auditwheel-emscripten # auditwheel-emscripten
# pyodide-build # pyodide-build
# pyodide-cli # pyodide-cli
types-requests==2.32.0.20240712 typing-extensions==4.13.2
# via pyodide-build
typing-extensions==4.12.2
# via # via
# anyio
# pydantic # pydantic
# pydantic-core # pydantic-core
# typer # typer
unearth==0.17.2 # typing-inspection
typing-inspection==0.4.0
# via pydantic
unearth==0.17.5
# via pyodide-build # via pyodide-build
urllib3==2.2.2 urllib3==2.4.0
# via # via requests
# requests virtualenv==20.30.0
# types-requests
virtualenv==20.26.3
# via # via
# build # build
# pyodide-build # pyodide-build
wheel==0.44.0 wheel==0.45.1
# via # via
# auditwheel-emscripten # auditwheel-emscripten
# pyodide-build # pyodide-build
@@ -2,33 +2,33 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
importlib-metadata==8.4.0 importlib-metadata==8.6.1
# via build # via build
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
tomli==2.0.1 tomli==2.2.1
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.20.1 zipp==3.21.0
# via importlib-metadata # via importlib-metadata
@@ -2,27 +2,27 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -2,27 +2,27 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -2,27 +2,27 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
@@ -6,7 +6,7 @@ build==1.1.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.12.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.12.2 filelock==3.12.2
# via virtualenv # via virtualenv
@@ -24,7 +24,7 @@ pip==24.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.0.0 platformdirs==4.0.0
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
tomli==2.0.1 tomli==2.0.1
# via build # via build
@@ -33,7 +33,7 @@ typing-extensions==4.7.1
# delocate # delocate
# importlib-metadata # importlib-metadata
# platformdirs # platformdirs
virtualenv==20.26.3 virtualenv==20.26.6
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.15.0 zipp==3.15.0
# via importlib-metadata # via importlib-metadata
+12 -12
View File
@@ -2,33 +2,33 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.12.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.16.1
# via virtualenv # via virtualenv
importlib-metadata==8.4.0 importlib-metadata==8.5.0
# via build # via build
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.6
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
tomli==2.0.1 tomli==2.2.1
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.20.1 zipp==3.20.2
# via importlib-metadata # via importlib-metadata
+13 -13
View File
@@ -2,33 +2,33 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
importlib-metadata==8.4.0 importlib-metadata==8.6.1
# via build # via build
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
tomli==2.0.1 tomli==2.2.1
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
zipp==3.20.1 zipp==3.21.0
# via importlib-metadata # via importlib-metadata
+10 -10
View File
@@ -2,27 +2,27 @@
# nox -s update_constraints # nox -s update_constraints
altgraph==0.17.4 altgraph==0.17.4
# via macholib # via macholib
build==1.2.1 build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
delocate==0.12.0 delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
distlib==0.3.8 distlib==0.3.9
# via virtualenv # via virtualenv
filelock==3.15.4 filelock==3.18.0
# via virtualenv # via virtualenv
macholib==1.16.3 macholib==1.16.3
# via delocate # via delocate
packaging==24.1 packaging==25.0
# via # via
# build # build
# delocate # delocate
pip==24.2 pip==25.0.1
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2 platformdirs==4.3.7
# via virtualenv # via virtualenv
pyproject-hooks==1.1.0 pyproject-hooks==1.2.0
# via build # via build
typing-extensions==4.12.2 typing-extensions==4.13.2
# via delocate # via delocate
virtualenv==20.26.3 virtualenv==20.30.0
# via -r cibuildwheel/resources/constraints.in # via -r cibuildwheel/resources/constraints.in
+4
View File
@@ -3,6 +3,7 @@ build = "*"
skip = "" skip = ""
test-skip = "" test-skip = ""
free-threaded-support = false free-threaded-support = false
enable = []
archs = ["auto"] archs = ["auto"]
build-frontend = "default" build-frontend = "default"
@@ -20,6 +21,7 @@ test-command = ""
before-test = "" before-test = ""
test-requires = [] test-requires = []
test-extras = [] test-extras = []
test-groups = []
container-engine = "docker" container-engine = "docker"
@@ -28,6 +30,7 @@ manylinux-i686-image = "manylinux2014"
manylinux-aarch64-image = "manylinux2014" manylinux-aarch64-image = "manylinux2014"
manylinux-ppc64le-image = "manylinux2014" manylinux-ppc64le-image = "manylinux2014"
manylinux-s390x-image = "manylinux2014" manylinux-s390x-image = "manylinux2014"
manylinux-armv7l-image = "manylinux_2_31"
manylinux-pypy_x86_64-image = "manylinux2014" manylinux-pypy_x86_64-image = "manylinux2014"
manylinux-pypy_i686-image = "manylinux2014" manylinux-pypy_i686-image = "manylinux2014"
manylinux-pypy_aarch64-image = "manylinux2014" manylinux-pypy_aarch64-image = "manylinux2014"
@@ -37,6 +40,7 @@ musllinux-i686-image = "musllinux_1_2"
musllinux-aarch64-image = "musllinux_1_2" musllinux-aarch64-image = "musllinux_1_2"
musllinux-ppc64le-image = "musllinux_1_2" musllinux-ppc64le-image = "musllinux_1_2"
musllinux-s390x-image = "musllinux_1_2" musllinux-s390x-image = "musllinux_1_2"
musllinux-armv7l-image = "musllinux_1_2"
[tool.cibuildwheel.linux] [tool.cibuildwheel.linux]
+2 -1
View File
@@ -1,2 +1,3 @@
url = "https://nodejs.org/dist/" url = "https://nodejs.org/dist/"
v20 = "v20.17.0" v22 = "v22.14.0"
v20 = "v20.19.0"
+28 -24
View File
@@ -1,54 +1,58 @@
[x86_64] [x86_64]
manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8 manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024.09.01-2 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.09.01-2 musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024.10.26-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2024.09.01-2 musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2025.04.19-1
[i686] [i686]
manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8 manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.09.01-2 musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2024.10.26-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2024.09.01-2 musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2025.04.19-1
[pypy_x86_64] [pypy_x86_64]
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2024.09.01-2 manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2025.04.19-1
[pypy_i686] [pypy_i686]
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_i686:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
[aarch64] [aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2024.09.01-2 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.09.01-2 musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024.10.26-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2024.09.01-2 musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2025.04.19-1
[ppc64le] [ppc64le]
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2024.09.01-2 manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2025.04.19-1
musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.09.01-2 musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024.10.26-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2024.09.01-2 musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2025.04.19-1
[s390x] [s390x]
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_s390x:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2024.09.01-2 manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2025.04.19-1
musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.09.01-2 musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024.10.26-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2024.09.01-2 musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2025.04.19-1
[pypy_aarch64] [pypy_aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024.09.01-2 manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2025.04.19-1
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463 manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2024.09.01-2 manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2025.04.19-1
[armv7l]
manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2025.04.19-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2025.04.19-1
+2 -2
View File
@@ -1,2 +1,2 @@
py36 = { version = "20.21.1", url = "https://github.com/pypa/get-virtualenv/blob/20.21.1/public/virtualenv.pyz?raw=true" } py36 = { version = "20.17.1", url = "https://bootstrap.pypa.io/virtualenv/3.6/virtualenv.pyz" }
default = { version = "20.26.3", url = "https://github.com/pypa/get-virtualenv/blob/20.26.3/public/virtualenv.pyz?raw=true" } default = { version = "20.26.6", url = "https://bootstrap.pypa.io/virtualenv/3.7/virtualenv.pyz" }
-1
View File
@@ -9,7 +9,6 @@ __all__ = (
"PLATFORMS", "PLATFORMS",
"PathOrStr", "PathOrStr",
"PlatformName", "PlatformName",
"PLATFORMS",
"PopenBytes", "PopenBytes",
) )
+104 -20
View File
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import contextlib import contextlib
import enum
import fnmatch import fnmatch
import itertools import itertools
import os import os
@@ -19,11 +20,11 @@ from collections import defaultdict
from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from functools import lru_cache from functools import lru_cache, total_ordering
from pathlib import Path, PurePath from pathlib import Path, PurePath
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from time import sleep from time import sleep
from typing import Any, ClassVar, Final, Literal, TextIO, TypeVar from typing import Any, Final, Literal, TextIO, TypeVar
from zipfile import ZipFile from zipfile import ZipFile
import bracex import bracex
@@ -37,10 +38,12 @@ from platformdirs import user_cache_path
from ._compat import tomllib from ._compat import tomllib
from .architecture import Architecture from .architecture import Architecture
from .errors import FatalError
from .typing import PathOrStr, PlatformName from .typing import PathOrStr, PlatformName
__all__ = [ __all__ = [
"MANYLINUX_ARCHS", "MANYLINUX_ARCHS",
"EnableGroups",
"call", "call",
"chdir", "chdir",
"combine_constraints", "combine_constraints",
@@ -66,6 +69,16 @@ free_thread_enable_313: Final[Path] = resources_dir / "free-threaded-enable-313.
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py" test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
class EnableGroups(enum.Enum):
"""
Groups of build selectors that are not enabled by default.
"""
CPythonFreeThreading = "cpython-freethreading"
CPythonPrerelease = "cpython-prerelease"
PyPy = "pypy"
MANYLINUX_ARCHS: Final[tuple[str, ...]] = ( MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64", "x86_64",
"i686", "i686",
@@ -73,6 +86,7 @@ MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"aarch64", "aarch64",
"ppc64le", "ppc64le",
"s390x", "s390x",
"armv7l",
"pypy_aarch64", "pypy_aarch64",
"pypy_i686", "pypy_i686",
) )
@@ -83,6 +97,7 @@ MUSLLINUX_ARCHS: Final[tuple[str, ...]] = (
"aarch64", "aarch64",
"ppc64le", "ppc64le",
"s390x", "s390x",
"armv7l",
) )
DEFAULT_CIBW_CACHE_PATH: Final[Path] = user_cache_path(appname="cibuildwheel", appauthor="pypa") DEFAULT_CIBW_CACHE_PATH: Final[Path] = user_cache_path(appname="cibuildwheel", appauthor="pypa")
@@ -126,13 +141,32 @@ def call(
args_ = [str(arg) for arg in args] args_ = [str(arg) for arg in args]
# print the command executing for the logs # print the command executing for the logs
print("+ " + " ".join(shlex.quote(a) for a in args_)) print("+ " + " ".join(shlex.quote(a) for a in args_))
kwargs: dict[str, Any] = {} # workaround platform behaviour differences outlined
if capture_stdout: # in https://github.com/python/cpython/issues/52803
kwargs["universal_newlines"] = True path_env = env if env is not None else os.environ
kwargs["stdout"] = subprocess.PIPE path = path_env.get("PATH", None)
result = subprocess.run(args_, check=True, shell=IS_WIN, env=env, cwd=cwd, **kwargs) executable = shutil.which(args_[0], path=path)
if executable is None:
msg = f"Couldn't find {args_[0]!r} in PATH {path!r}"
raise FatalError(msg)
args_[0] = executable
try:
result = subprocess.run(
args_,
check=True,
shell=IS_WIN,
env=env,
cwd=cwd,
capture_output=capture_stdout,
text=capture_stdout,
)
except subprocess.CalledProcessError as e:
if capture_stdout:
sys.stderr.write(e.stderr)
raise
if not capture_stdout: if not capture_stdout:
return None return None
sys.stderr.write(result.stderr)
return typing.cast(str, result.stdout) return typing.cast(str, result.stdout)
@@ -246,12 +280,7 @@ class BuildSelector:
build_config: str build_config: str
skip_config: str skip_config: str
requires_python: SpecifierSet | None = None requires_python: SpecifierSet | None = None
enable: frozenset[EnableGroups] = frozenset()
# a pattern that skips prerelease versions, when include_prereleases is False.
PRERELEASE_SKIP: ClassVar[str] = ""
prerelease_pythons: bool = False
free_threaded_support: bool = False
def __call__(self, build_id: str) -> bool: def __call__(self, build_id: str) -> bool:
# Filter build selectors by python_requires if set # Filter build selectors by python_requires if set
@@ -265,12 +294,16 @@ class BuildSelector:
if not self.requires_python.contains(version): if not self.requires_python.contains(version):
return False return False
# filter out the prerelease pythons if self.prerelease_pythons is False # filter out groups that are not enabled
if not self.prerelease_pythons and selector_matches(self.PRERELEASE_SKIP, build_id): if EnableGroups.CPythonFreeThreading not in self.enable and selector_matches(
"cp3??t-*", build_id
):
return False return False
if EnableGroups.CPythonPrerelease not in self.enable and selector_matches(
# filter out free threaded pythons if self.free_threaded_support is False "cp314*", build_id
if not self.free_threaded_support and selector_matches("*t-*", build_id): ):
return False
if EnableGroups.PyPy not in self.enable and selector_matches("pp*", build_id):
return False return False
should_build = selector_matches(self.build_config, build_id) should_build = selector_matches(self.build_config, build_id)
@@ -283,8 +316,7 @@ class BuildSelector:
"build_config": self.build_config, "build_config": self.build_config,
"skip_config": self.skip_config, "skip_config": self.skip_config,
"requires_python": str(self.requires_python), "requires_python": str(self.requires_python),
"prerelease_pythons": self.prerelease_pythons, "enable": sorted(group.value for group in self.enable),
"free_threaded_support": self.free_threaded_support,
} }
@@ -673,6 +705,10 @@ def virtualenv(
dependency_constraint_flags are ignored since nothing is installed in the dependency_constraint_flags are ignored since nothing is installed in the
venv. Otherwise, pip is installed, and setuptools + wheel if Python < 3.12. venv. Otherwise, pip is installed, and setuptools + wheel if Python < 3.12.
""" """
# virtualenv may fail if this is a symlink.
python = python.resolve()
assert python.exists() assert python.exists()
if use_uv: if use_uv:
@@ -899,3 +935,51 @@ def combine_constraints(
env["UV_CONSTRAINT"] = env["PIP_CONSTRAINT"] = " ".join( env["UV_CONSTRAINT"] = env["PIP_CONSTRAINT"] = " ".join(
c for c in [our_constraints, user_constraints] if c c for c in [our_constraints, user_constraints] if c
) )
@total_ordering
class FlexibleVersion:
version_str: str
version_parts: tuple[int, ...]
suffix: str
def __init__(self, version_str: str) -> None:
self.version_str = version_str
# Split into numeric parts and the optional suffix
match = re.match(r"^[v]?(\d+(\.\d+)*)(.*)$", version_str)
if not match:
msg = f"Invalid version string: {version_str}"
raise ValueError(msg)
version_part, _, suffix = match.groups()
# Convert numeric version part into a tuple of integers
self.version_parts = tuple(map(int, version_part.split(".")))
self.suffix = suffix.strip() if suffix else ""
# Normalize by removing trailing zeros
self.version_parts = self._remove_trailing_zeros(self.version_parts)
def _remove_trailing_zeros(self, parts: tuple[int, ...]) -> tuple[int, ...]:
# Remove trailing zeros for accurate comparisons
# without this, "3.0" would be considered greater than "3"
while parts and parts[-1] == 0:
parts = parts[:-1]
return parts
def __eq__(self, other: object) -> bool:
if not isinstance(other, FlexibleVersion):
raise NotImplementedError()
return (self.version_parts, self.suffix) == (other.version_parts, other.suffix)
def __lt__(self, other: object) -> bool:
if not isinstance(other, FlexibleVersion):
raise NotImplementedError()
return (self.version_parts, self.suffix) < (other.version_parts, other.suffix)
def __repr__(self) -> str:
return f"FlexibleVersion('{self.version_str}')"
def __str__(self) -> str:
return self.version_str
+21 -15
View File
@@ -225,6 +225,14 @@ def setup_rust_cross_compile(
) )
def can_use_uv(python_configuration: PythonConfiguration) -> bool:
conditions = (
Version(python_configuration.version) >= Version("3.8"),
not python_configuration.identifier.startswith("pp38-"),
)
return all(conditions)
def setup_python( def setup_python(
tmp: Path, tmp: Path,
python_configuration: PythonConfiguration, python_configuration: PythonConfiguration,
@@ -254,11 +262,10 @@ def setup_python(
raise ValueError(msg) raise ValueError(msg)
assert base_python.exists() assert base_python.exists()
use_uv = ( if build_frontend == "build[uv]" and not can_use_uv(python_configuration):
build_frontend == "build[uv]" build_frontend = "build"
and Version(python_configuration.version) >= Version("3.8")
and not python_configuration.identifier.startswith("pp38-") use_uv = build_frontend == "build[uv]"
)
uv_path = find_uv() uv_path = find_uv()
log.step("Setting up build environment...") log.step("Setting up build environment...")
@@ -295,22 +302,22 @@ def setup_python(
env = environment.as_dictionary(prev_environment=env) env = environment.as_dictionary(prev_environment=env)
# check what Python version we're on # check what Python version we're on
call("where", "python", env=env)
call("python", "--version", env=env)
call("python", "-c", "\"import struct; print(struct.calcsize('P') * 8)\"", env=env)
where_python = call("where", "python", env=env, capture_stdout=True).splitlines()[0].strip() where_python = call("where", "python", env=env, capture_stdout=True).splitlines()[0].strip()
print(where_python)
if where_python != str(venv_path / "Scripts" / "python.exe"): if where_python != str(venv_path / "Scripts" / "python.exe"):
msg = "python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it." msg = "python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it."
raise errors.FatalError(msg) raise errors.FatalError(msg)
call("python", "--version", env=env)
call("python", "-c", "\"import struct; print(struct.calcsize('P') * 8)\"", env=env)
# check what pip version we're on # check what pip version we're on
if not use_uv: if not use_uv:
assert (venv_path / "Scripts" / "pip.exe").exists() assert (venv_path / "Scripts" / "pip.exe").exists()
where_pip = call("where", "pip", env=env, capture_stdout=True).splitlines()[0].strip() where_pip = call("where", "pip", env=env, capture_stdout=True).splitlines()[0].strip()
print(where_pip)
if where_pip.strip() != str(venv_path / "Scripts" / "pip.exe"): if where_pip.strip() != str(venv_path / "Scripts" / "pip.exe"):
msg = "pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it." msg = "pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it."
raise errors.FatalError(msg) raise errors.FatalError(msg)
call("pip", "--version", env=env) call("pip", "--version", env=env)
log.step("Installing build tools...") log.step("Installing build tools...")
@@ -368,11 +375,7 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations: for config in python_configurations:
build_options = options.build_options(config.identifier) build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip") build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
use_uv = ( use_uv = build_frontend.name == "build[uv]" and can_use_uv(config)
build_frontend.name == "build[uv]"
and Version(config.version) >= Version("3.8")
and not config.identifier.startswith("pp38-")
)
log.build_start(config.identifier) log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier identifier_tmp_dir = tmp_path / config.identifier
@@ -487,7 +490,10 @@ def build(options: Options, tmp_path: Path) -> None:
else: else:
shutil.move(str(built_wheel), repaired_wheel_dir) shutil.move(str(built_wheel), repaired_wheel_dir)
repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) try:
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
except StopIteration:
raise errors.RepairStepProducedNoWheelError() from None
if repaired_wheel.name in {wheel.name for wheel in built_wheels}: if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name) raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
+105 -2
View File
@@ -4,8 +4,103 @@ title: Changelog
# Changelog # Changelog
### v2.23.4
_16 March 2026_
- 🐛 Fix HTTP 429 errors while downloading virtualenv from GitHub blob URLs uses a https://bootstrap.pypa.io/ URL instead. (#2775)
### v2.23.3
_26 April 2025_
- 🛠 Dependency updates, including Python 3.13.3 (#2371)
### v2.23.2
_24 March 2025_
- 🐛 Workaround an issue with pyodide builds when running cibuildwheel with a Python that was installed via UV (#2328 via #2331)
- 🛠 Dependency updates, including a manylinux update that fixes an ['undefined symbol' error](https://github.com/pypa/manylinux/issues/1760) in gcc-toolset (#2334)
### v2.23.1
_15 March 2025_
- ⚠️ Added warnings when the shorthand values `manylinux1`, `manylinux2010`, `manylinux_2_24`, and `musllinux_1_1` are used to specify the images in linux builds. The shorthand to these (unmaintainted) images will be removed in v3.0. If you want to keep using these images, explicitly opt-in using the full image URL, which can be found in [this file](https://github.com/pypa/cibuildwheel/blob/v2.23.1/cibuildwheel/resources/pinned_docker_images.cfg). (#2312)
- 🛠 Dependency updates, including a manylinux update which fixes an [issue with rustup](https://github.com/pypa/cibuildwheel/issues/2303). (#2315)
### v2.23.0
_1 March 2025_
- ✨ Adds official support for the new GitHub Actions Arm runners. In fact these worked out-of-the-box, now we include them in our tests and example configs. (#2135 via #2281)
- ✨ Adds support for building PyPy 3.11 wheels (#2268 via #2281)
- 🛠 Adopts the beta pypa/manylinux image for armv7l builds (#2269 via #2281)
- 🛠 Dependency updates, including Pyodide 0.27 (#2117 and #2281)
### v2.22.0
_23 November 2024_
- 🌟 Added a new `CIBW_ENABLE`/`enable` feature that replaces `CIBW_FREETHREADED_SUPPORT`/`free-threaded-support` and `CIBW_PRERELEASE_PYTHONS` with a system that supports both. In cibuildwheel 3, this will also include a PyPy setting and the deprecated options will be removed. (#2048)
- 🌟 [Dependency groups](https://peps.python.org/pep-0735/) are now supported for tests. Use `CIBW_TEST_GROUPS`/`test-groups` to specify groups in `[dependency-groups]` for testing. (#2063)
- 🌟 Support for the experimental Ubuntu-based ARMv7l manylinux image (#2052)
- ✨ Show a warning when cibuildwheel is run from Python 3.10 or older; cibuildwheel 3.0 will require Python 3.11 or newer as host (#2050)
- 🐛 Fix issue with stderr interfering with checking the docker version (#2074)
- 🛠 Python 3.9 is now used in `CIBW_BEFORE_ALL`/`before-all` on linux, replacing 3.8, which is now EoL (#2043)
- 🛠 Error messages for producing a pure-Python wheel are slightly more informative (#2044)
- 🛠 Better error when `uname -m` fails on ARM (#2049)
- 🛠 Better error when repair fails and docs for abi3audit on Windows (#2058)
- 🛠 Better error when `manylinux-interpreters ensure` fails (#2066)
- 🛠 Update Pyodide to 0.26.4, and adapt to the unbundled pyodide-build (now 0.29) (#2090)
- 🛠 Now cibuildwheel uses dependency-groups for development dependencies (#2064, #2085)
- 📚 Docs updates and tidy ups (#2061, #2067, #2072)
### v2.21.3
_9 October 2024_
- 🛠 Update CPython 3.13 to 3.13.0 final release (#2032)
- 📚 Docs updates and tidy ups (#2035)
### v2.21.2
_2 October 2024_
- ✨ Adds support for building 32-bit armv7l wheels on musllinux. On a Linux system with emulation set up, set [CIBW_ARCHS](https://cibuildwheel.pypa.io/en/stable/options/#archs) to `armv7l` on Linux to try it out if you're interested! (#2017)
- 🐛 Fix Linux Podman builds on some systems (#2016)
- ✨ Adds official support for running on Python 3.13 (#2026)
- 🛠 Update CPython 3.13 to 3.13.0rc3 (#2029)
Note: the default [manylinux image](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) is **scheduled to change** from `manylinux2014` to `manylinux_2_28` in a cibuildwheel release on or after **6th May 2025** - you can set the value now to avoid getting upgraded if you want. (#1992)
### v2.21.1
_16 September 2024_
- 🐛 Fix a bug in the Linux build, where files copied to the container would have invalid ownership permissions (#2007)
- 🐛 Fix a bug on Windows where cibuildwheel would call upon `uv` to install dependencies for versions of CPython that it does not support (#2005)
- 🐛 Fix a bug where `uv 0.4.10` would not use the right Python when testing on Linux. (#2008)
- 🛠 Bump our documentation pins, fixes an issue with a missing package (#2011)
### v2.21.0
_13 September 2024_
- ⚠️ Update CPython 3.12 to 3.12.6, which changes the macOS minimum deployment target on CPython 3.12 from macOS 10.9 to macOS 10.13 (#1998)
- 🛠 Changes the behaviour when inheriting `config-settings` in TOML overrides - rather than extending each key, which is rarely useful, individual keys will override previously set values. (#1803)
- 🛠 Update CPython 3.13 to 3.13.0rc2 (#1998)
- ✨ Adds support for multiarch OCI images (#1961)
- 🐛 Fixes some bugs building Linux wheels on macOS. (#1961)
- ⚠️ Changes the minimum version of Docker/Podman to Docker API version 1.43, Podman API version 3. The only mainstream runner this should affect is Travis Graviton2 runners - if so you can [upgrade your version of Docker](https://github.com/pypa/cibuildwheel/pull/1961#issuecomment-2304060019). (#1961)
### v2.20.0 ### v2.20.0
_4 August 2024_
- 🌟 CPython 3.13 wheels are now built by default - without the `CIBW_PRERELEASE_PYTHONS` flag. It's time to build and upload these wheels to PyPI! This release includes CPython 3.13.0rc1, which is guaranteed to be ABI compatible with the final release. Free-threading is still behind a flag/config option. (#1950) - 🌟 CPython 3.13 wheels are now built by default - without the `CIBW_PRERELEASE_PYTHONS` flag. It's time to build and upload these wheels to PyPI! This release includes CPython 3.13.0rc1, which is guaranteed to be ABI compatible with the final release. Free-threading is still behind a flag/config option. (#1950)
- ✨ Provide a `CIBW_ALLOW_EMPTY` environment variable as an alternative to the command line flag. (#1937) - ✨ Provide a `CIBW_ALLOW_EMPTY` environment variable as an alternative to the command line flag. (#1937)
- 🐛 Don't use uv on PyPy3.8 on Windows, it stopped working starting in 0.2.25. Note that PyPy 3.8 is EoL. (#1868) - 🐛 Don't use uv on PyPy3.8 on Windows, it stopped working starting in 0.2.25. Note that PyPy 3.8 is EoL. (#1868)
@@ -16,6 +111,8 @@ title: Changelog
### v2.19.2 ### v2.19.2
_2 July 2024_
- 🐛 Update manylinux2014 pins to versions that support past-EoL CentOS 7 mirrors. (#1917) - 🐛 Update manylinux2014 pins to versions that support past-EoL CentOS 7 mirrors. (#1917)
- 🐛 Support `--no-isolation` with `build[uv]` build-frontend. (#1889) - 🐛 Support `--no-isolation` with `build[uv]` build-frontend. (#1889)
- 🛠 Provide attestations for releases at <https://github.com/pypa/cibuildwheel/attestations>. (#1916) - 🛠 Provide attestations for releases at <https://github.com/pypa/cibuildwheel/attestations>. (#1916)
@@ -28,6 +125,8 @@ title: Changelog
### v2.19.1 ### v2.19.1
_13 June 2024_
- 🐛 Don't require setup-python on GHA for Pyodide (#1868) - 🐛 Don't require setup-python on GHA for Pyodide (#1868)
- 🐛 Specify full python path for uv (fixes issue in 0.2.10 & 0.2.11) (#1881) - 🐛 Specify full python path for uv (fixes issue in 0.2.10 & 0.2.11) (#1881)
- 🛠 Update for pip 24.1b2 on CPython 3.13. (#1879) - 🛠 Update for pip 24.1b2 on CPython 3.13. (#1879)
@@ -37,6 +136,8 @@ title: Changelog
### v2.19.0 ### v2.19.0
_10 June 2024_
See the [release post](https://iscinumpy.dev/post/cibuildwheel-2-19-0/) for more info on new features! See the [release post](https://iscinumpy.dev/post/cibuildwheel-2-19-0/) for more info on new features!
- 🌟 Add Pyodide platform. Set with `--platform pyodide` or `CIBW_PLATFORM: pyodide` on Linux with a host Python 3.12 to build WebAssembly wheels. Not accepted on PyPI currently, but usable directly in a website using Pyodide, for live docs, etc. (#1456, #1859) - 🌟 Add Pyodide platform. Set with `--platform pyodide` or `CIBW_PLATFORM: pyodide` on Linux with a host Python 3.12 to build WebAssembly wheels. Not accepted on PyPI currently, but usable directly in a website using Pyodide, for live docs, etc. (#1456, #1859)
@@ -55,6 +156,8 @@ See the [release post](https://iscinumpy.dev/post/cibuildwheel-2-19-0/) for more
### v2.18.1 ### v2.18.1
_20 May 2024_
- 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831) - 🌟 Add free-threaded Linux and Windows builds for 3.13. New identifiers `cp313t-*`, new option `CIBW_FREE_THREADED_SUPPORT`/`tool.cibuildwheel.free-threaded-support` required to opt-in. [See the docs](https://cibuildwheel.pypa.io/en/stable/options/#free-threaded-support) for more information. (#1831)
- ✨ The `container-engine` is now a build (non-global) option. (#1792) - ✨ The `container-engine` is now a build (non-global) option. (#1792)
- 🛠 The build backend for cibuildwheel is now hatchling. (#1297) - 🛠 The build backend for cibuildwheel is now hatchling. (#1297)
@@ -88,7 +191,7 @@ _12 May 2024_
_11 March 2024_ _11 March 2024_
- 🌟 Adds the ability to inherit configuration in TOML overrides. This makes certain configurations much simpler. If you're overriding an option like `before-build` or `environment`, and you just want to add an extra command or environment variable, you can just append (or prepend) to the previous config. See [the docs](https://cibuildwheel.pypa.io/en/stable/options/#inherit) for more information. (#1730) - 🌟 Adds the ability to inherit configuration in TOML overrides. This makes certain configurations much simpler. If you're overriding an option like `before-build` or `environment`, and you just want to add an extra command or environment variable, you can just append (or prepend) to the previous config. See [the docs](https://cibuildwheel.pypa.io/en/stable/options/#inherit) for more information. (#1730)
- 🌟 Adds official support for native `arm64` macOS GitHub runners. To use them, just specify `macos-14` as an `os` of your job in your workflow file. You can also keep `macos-13` in your build matrix to build `x86_64`. Check out the new [GitHub Actions example config](https://cibuildwheel.pypa.io/en/stable/setup/#github-actions). - 🌟 Adds official support for native `arm64` macOS GitHub runners. To use them, just specify `macos-latest` as an `os` of your job in your workflow file. You can also keep `macos-15-intel` in your build matrix to build `x86_64`. Check out the new [GitHub Actions example config](https://cibuildwheel.pypa.io/en/stable/setup/#github-actions).
- ✨ You no longer need to specify `--platform` to run cibuildwheel locally! Instead it will detect your platform automatically. This was a safety feature, no longer necessary. (#1727) - ✨ You no longer need to specify `--platform` to run cibuildwheel locally! Instead it will detect your platform automatically. This was a safety feature, no longer necessary. (#1727)
- 🛠 Removed setuptools and wheel pinned versions. This only affects old-style projects without a `pyproject.toml`, projects with `pyproject.toml` are already getting fresh versions of their `build-system.requires` installed into an isolated environment. (#1725) - 🛠 Removed setuptools and wheel pinned versions. This only affects old-style projects without a `pyproject.toml`, projects with `pyproject.toml` are already getting fresh versions of their `build-system.requires` installed into an isolated environment. (#1725)
- 🛠 Improve how the GitHub Action passes arguments (#1757) - 🛠 Improve how the GitHub Action passes arguments (#1757)
@@ -105,7 +208,7 @@ _11 March 2024_
_30 January 2024_ _30 January 2024_
- 🐛 Fix an incompatibility with the GitHub Action and new GitHub Runner images for Windows that bundle Powershell 7.3+ (#1741) - 🐛 Fix an incompatibility with the GitHub Action and new GitHub Runner images for Windows that bundle Powershell 7.3+ (#1741)
- 🛠 Preliminary support for new `macos-14` arm64 runners (#1743) - 🛠 Preliminary support for new `macos-latest` arm64 runners (#1743)
### v2.16.4 ### v2.16.4
+28 -10
View File
@@ -17,11 +17,11 @@ Everyone contributing to the cibuildwheel project is expected to follow the [PSF
- `cibuildwheel` should wrap the complexity of wheel building. - `cibuildwheel` should wrap the complexity of wheel building.
- The user interface to `cibuildwheel` is the build script (e.g. `.travis.yml`). Feature additions should not increase the complexity of this script. - The user interface to `cibuildwheel` is the build script (e.g. `.travis.yml`). Feature additions should not increase the complexity of this script.
- Options should be environment variables (these lend themselves better to YML config files). They should be prefixed with `CIBW_`. - Options should be environment variables (these lend themselves better to YML config files). They should be prefixed with `CIBW_`.
- Options should be generalise to all platforms. If platform-specific options are required, they should be namespaced e.g. `CIBW_TEST_COMMAND_MACOS` - Options should be generalised to all platforms. If platform-specific options are required, they should be namespaced e.g. `CIBW_TEST_COMMAND_MACOS`
Other notes: Other notes:
- The platforms are very similar, until they're not. I'd rather have straight-forward code than totally DRY code, so let's keep airy platform abstractions to a minimum. - The platforms are very similar, until they're not. I'd rather have straightforward code than totally DRY code, so let's keep airy platform abstractions to a minimum.
- I might want to break the options into a shared config file one day, so that config is more easily shared. That has motivated some of the design decisions. - I might want to break the options into a shared config file one day, so that config is more easily shared. That has motivated some of the design decisions.
### cibuildwheel's relationship with build errors ### cibuildwheel's relationship with build errors
@@ -82,14 +82,11 @@ A few notes-
- Running the macOS integration tests requires _system installs_ of Python from python.org for all the versions that are tested. We won't attempt to install these when running locally, but you can do so manually using the URL in the error message that is printed when the install is not found. - Running the macOS integration tests requires _system installs_ of Python from python.org for all the versions that are tested. We won't attempt to install these when running locally, but you can do so manually using the URL in the error message that is printed when the install is not found.
#### Making a venv #### Running pytest directly
More advanced users might prefer to invoke pytest directly- More advanced users might prefer to invoke pytest directly. Set up a [dev environment](#setting-up-a-dev-environment), then,
```bash ```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e .[dev]
# run the unit tests # run the unit tests
pytest unit_test pytest unit_test
# run the whole integration test suite # run the whole integration test suite
@@ -102,7 +99,7 @@ CIBW_PLATFORM=linux pytest test -k test_build_frontend_args
### Linting, docs ### Linting, docs
Most developer tasks have a nox interface. This allows you to very simply run tasks without worrying about setting up a development environment (as shown below). This is a slower than setting up a development environment and reusing it, but has the (important) benefit of being highly reproducible; an earlier run does not affect a current run, or anything else on your machine. Most developer tasks have a nox interface. This allows you to very simply run tasks without worrying about setting up a development environment (as shown below). This is slower than setting up a development environment and reusing it, but has the (important) benefit of being highly reproducible; an earlier run does not affect a current run, or anything else on your machine.
You can see a list of sessions by typing `nox -l`; here are a few common ones: You can see a list of sessions by typing `nox -l`; here are a few common ones:
@@ -113,12 +110,33 @@ nox -s docs # Build and serve the documentation
nox -s build # Make SDist and wheel nox -s build # Make SDist and wheel
``` ```
More advanced users can run the update scripts. `update_pins` should work directly, but `update_constraints` needs all versions of Python installed. If you don't want to do that locally, a fast way to run it to use docker to run nox: More advanced users can run the update scripts:
```console ```console
docker run --rm -itv $PWD:/src -w /src quay.io/pypa/manylinux_2_24_x86_64:latest pipx run nox -s update_constraints nox -s update_constraints # update all constraints files in cibuildwheel/resources
nox -s update_pins # update tools, python interpreters & docker images used by cibuildwheel
``` ```
### Setting up a dev environment
A dev environment isn't required for any of the `nox` tasks above. However, a dev environment is still useful, to be able to point an editor at, and a few other jobs.
cibuildwheel uses dependency groups. Set up a dev environment with UV by doing
```bash
uv sync
```
Or, if you're not using `uv`, you can do:
```bash
python3 -m venv .venv
source .venv/bin/activate
pipx run dependency-groups dev | xargs pip install -e.
```
Your virtualenv is at `.venv`.
## Maintainer notes ## Maintainer notes
### Testing sample configs ### Testing sample configs
+2 -3
View File
@@ -8,7 +8,7 @@ Building Python wheels with modern C++ standards (C++11 and later) requires a fe
## manylinux1 and C++14 ## manylinux1 and C++14
The old `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118 The past end-of-life `manylinux1` image (based on CentOS 5) contains a version of GCC and libstdc++ that only supports C++11 and earlier standards. There are however ways to compile wheels with the C++14 standard (and later): https://github.com/pypa/manylinux/issues/118
`manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17). `manylinux2010` and `manylinux2014` are newer and support all C++ standards (up to C++17).
@@ -18,9 +18,8 @@ OS X/macOS allows you to specify a so-called "deployment target" version that wi
However, to enable modern C++ standards, the deployment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library). However, to enable modern C++ standards, the deployment target needs to be set high enough (since older OS X/macOS versions did not have the necessary modern C++ standard library).
To get C++11 and C++14 support, `MACOSX_DEPLOYMENT_TARGET` needs to be set to (at least) `"10.9"`. By default, `cibuildwheel` already does this, building 64-bit-only wheels for macOS 10.9 and later.
To get C++17 support, Xcode 9.3+ is needed, requiring at least macOS 10.13 on the build machine. To use C++17 library features and link against the C++ runtime library, set `MACOSX_DEPLOYMENT_TARGET` to `"10.13"` or `"10.14"` (or higher) - macOS 10.13 offers partial C++17 support (e.g., the filesystem header is in experimental, offering `#include <experimental/filesystem>` instead of `#include <filesystem>`); macOS 10.14 has full C++17 support. To get C++17 support, Xcode 9.3+ is needed, requiring at least macOS 10.13 on the build machine. To use C++17 library features and link against the C++ runtime library, set `MACOSX_DEPLOYMENT_TARGET` to `"10.13"` or `"10.14"` (or higher) - macOS 10.13 offers partial C++17 support (e.g., the filesystem header is in experimental, offering `#include <experimental/filesystem>` instead of `#include <filesystem>`); macOS 10.14 has full C++17 support. CPython 3.12+ require 10.13+ anyway.
However, if only C++17 compiler and standard template library (STL) features are used (not needing a C++17 runtime) it might be possible to set `MACOSX_DEPLOYMENT_TARGET` to a lower value, such as `"10.9"`. To find out if this is the case, try compiling and running with a lower `MACOSX_DEPLOYMENT_TARGET`: if C++17 features are used that require a more recent deployment target, building the wheel should fail. However, if only C++17 compiler and standard template library (STL) features are used (not needing a C++17 runtime) it might be possible to set `MACOSX_DEPLOYMENT_TARGET` to a lower value, such as `"10.9"`. To find out if this is the case, try compiling and running with a lower `MACOSX_DEPLOYMENT_TARGET`: if C++17 features are used that require a more recent deployment target, building the wheel should fail.
+1 -1
View File
@@ -86,7 +86,7 @@ This requires setting this GitHub workflow in your project's PyPI settings (for
You should use Dependabot to keep the publish action up to date. In the above You should use Dependabot to keep the publish action up to date. In the above
example, the same name (the default, "artifact" is used for all upload-artifact example, the same name (the default, "artifact" is used for all upload-artifact
runs, so we can just download all of the in one step into a common directory. runs, so we can just download all of them in one step into a common directory.
See See
[`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml) [`examples/github-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml)
+10
View File
@@ -258,6 +258,16 @@ h1, h2, h3, h4, h5, h6 {
font-size: 80%; font-size: 80%;
} }
.rst-content table.docutils td code,
.rst-content table.docutils th code,
.rst-content table.field-list td code,
.rst-content table.field-list th code,
.wy-table td code,
.wy-table th code {
/* table elements are already made smaller, the code styling on top of that makes the text too small */
font-size: 82.5%;
}
/* expand all the toctree entries */ /* expand all the toctree entries */
.wy-menu-vertical .toctree-l1.current .toctree-l2>ul, .wy-menu-vertical .toctree-l1.current .toctree-l2>ul,
+8 -8
View File
@@ -10,7 +10,7 @@ title: Tips and tricks
Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 600](https://www.python.org/dev/peps/pep-0600/) / [PEP 656](https://www.python.org/dev/peps/pep-0656/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account: Linux wheels are built in [`manylinux`/`musllinux` containers](https://github.com/pypa/manylinux) to provide binary compatible wheels on Linux, according to [PEP 600](https://www.python.org/dev/peps/pep-0600/) / [PEP 656](https://www.python.org/dev/peps/pep-0656/). Because of this, when building with `cibuildwheel` on Linux, a few things should be taken into account:
- Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2010` or `manylinux2014`, `apt-get` for `manylinux_2_24`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building. - Programs and libraries are not installed on the CI runner host, but rather should be installed inside the container - using `yum` for `manylinux2010` or `manylinux2014`, `apt-get` for `manylinux_2_24`/`manylinux_2_31`, `dnf` for `manylinux_2_28` and `apk` for `musllinux_1_1` or `musllinux_1_2`, or manually. The same goes for environment variables that are potentially needed to customize the wheel building.
`cibuildwheel` supports this by providing the [`CIBW_ENVIRONMENT`](options.md#environment) and [`CIBW_BEFORE_ALL`](options.md#before-all) options to setup the build environment inside the running container. `cibuildwheel` supports this by providing the [`CIBW_ENVIRONMENT`](options.md#environment) and [`CIBW_BEFORE_ALL`](options.md#before-all) options to setup the build environment inside the running container.
@@ -72,7 +72,7 @@ See [GitHub issue 1333](https://github.com/pypa/cibuildwheel/issues/1333) for mo
It's easiest to build `x86_64` wheels on `x86_64` runners, and `arm64` wheels on `arm64` runners. It's easiest to build `x86_64` wheels on `x86_64` runners, and `arm64` wheels on `arm64` runners.
On GitHub Actions, `macos-14` runners are `arm64`, and `macos-13` runners are `x86_64`. So all you need to do is ensure both are in your build matrix. On GitHub Actions, `macos-latest` runners are `arm64`, and `macos-15-intel` runners are `x86_64`. So all you need to do is ensure both are in your build matrix.
#### Cross-compiling #### Cross-compiling
@@ -133,7 +133,7 @@ myextension = Extension(
### Automatic updates using Dependabot {: #automatic-updates} ### Automatic updates using Dependabot {: #automatic-updates}
Selecting a moving target (like the latest release) is generally a bad idea in CI. If something breaks, you can't tell whether it was your code or an upstream update that caused the breakage, and in a worse-case scenario, it could occur during a release. Selecting a moving target (like the latest release) is generally a bad idea in CI. If something breaks, you can't tell whether it was your code or an upstream update that caused the breakage, and in a worst-case scenario, it could occur during a release.
There are two suggested methods for keeping cibuildwheel up to date that instead involve scheduled pull requests using GitHub's Dependabot. There are two suggested methods for keeping cibuildwheel up to date that instead involve scheduled pull requests using GitHub's Dependabot.
@@ -142,7 +142,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
If you use GitHub Actions for builds, you can use cibuildwheel as an action: If you use GitHub Actions for builds, you can use cibuildwheel as an action:
```yaml ```yaml
uses: pypa/cibuildwheel@v2.20.0 uses: pypa/cibuildwheel@v2.23.4
``` ```
This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal. This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -164,7 +164,7 @@ The second option, and the only one that supports other CI systems, is using a `
```bash ```bash
# requirements-cibw.txt # requirements-cibw.txt
cibuildwheel==2.20.0 cibuildwheel==2.23.4
``` ```
Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this: Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -184,7 +184,7 @@ This will also try to update other pins in all requirement files, so be sure you
### Alternatives to cibuildwheel options {: #cibw-options-alternatives} ### Alternatives to cibuildwheel options {: #cibw-options-alternatives}
cibuildwheel provides lots of opportunities to configure the build cibuildwheel provides lots of opportunities to configure the build
environment. However, you might consider adding this build configuration into environment. However, you might consider adding this build configuration into
the package itself - in general, this is preferred, because users of your the package itself - in general, this is preferred, because users of your
package 'sdist' will also benefit. package 'sdist' will also benefit.
@@ -328,7 +328,7 @@ Solutions to this vary, but the simplest is to use pipx:
# most runners have pipx preinstalled, but in case you don't # most runners have pipx preinstalled, but in case you don't
python3 -m pip install pipx python3 -m pip install pipx
pipx run cibuildwheel==2.20.0 --output-dir wheelhouse pipx run cibuildwheel==2.23.4 --output-dir wheelhouse
pipx run twine upload wheelhouse/*.whl pipx run twine upload wheelhouse/*.whl
``` ```
@@ -363,7 +363,7 @@ If you're building on an arm64 runner, you might notice something strange about
This is fine for simple C extensions, but for more complicated builds on arm64 it becomes an issue. This is fine for simple C extensions, but for more complicated builds on arm64 it becomes an issue.
So, if you want to build macOS arm64 wheels on an arm64 runner (e.g., `macos-14`) on Python 3.8, before invoking cibuildwheel, you should install a native arm64 Python 3.8 interpreter on the runner: So, if you want to build macOS arm64 wheels on an arm64 runner (e.g., `macos-latest`) on Python 3.8, before invoking cibuildwheel, you should install a native arm64 Python 3.8 interpreter on the runner:
!!! tab "GitHub Actions" !!! tab "GitHub Actions"
+1 -1
View File
@@ -9,7 +9,7 @@ from typing import Any
def define_env(env: Any) -> None: def define_env(env: Any) -> None:
"Hook function for mkdocs-macros" "Hook function for mkdocs-macros"
@env.macro @env.macro # type: ignore[misc]
def subprocess_run(*args: str) -> str: def subprocess_run(*args: str) -> str:
"Run a subprocess and return the stdout" "Run a subprocess and return the stdout"
env = os.environ.copy() env = os.environ.copy()
+167 -98
View File
@@ -288,25 +288,36 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
<div class="build-id-table-marker"></div> <div class="build-id-table-marker"></div>
| | macOS | Windows | Linux Intel | Linux Other | | | macOS | Windows | Linux Intel | Linux Other |
|---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------|------------------------------------------------------------------------|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Python 3.6 | cp36-macosx_x86_64 | cp36-win_amd64<br/>cp36-win32 | cp36-manylinux_x86_64<br/>cp36-manylinux_i686<br/>cp36-musllinux_x86_64<br/>cp36-musllinux_i686 | cp36-manylinux_aarch64<br/>cp36-manylinux_ppc64le<br/>cp36-manylinux_s390x<br/>cp36-musllinux_aarch64<br/>cp36-musllinux_ppc64le<br/>cp36-musllinux_s390x | | Python 3.6 | cp36-macosx_x86_64 | cp36-win_amd64<br/>cp36-win32 | cp36-manylinux_x86_64<br/>cp36-manylinux_i686<br/>cp36-musllinux_x86_64<br/>cp36-musllinux_i686 | cp36-manylinux_aarch64<br/>cp36-manylinux_ppc64le<br/>cp36-manylinux_s390x<br/>cp36-manylinux_armv7l<br/>cp36-musllinux_aarch64<br/>cp36-musllinux_ppc64le<br/>cp36-musllinux_s390x<br/>cp36-musllinux_armv7l |
| Python 3.7 | cp37-macosx_x86_64 | cp37-win_amd64<br/>cp37-win32 | cp37-manylinux_x86_64<br/>cp37-manylinux_i686<br/>cp37-musllinux_x86_64<br/>cp37-musllinux_i686 | cp37-manylinux_aarch64<br/>cp37-manylinux_ppc64le<br/>cp37-manylinux_s390x<br/>cp37-musllinux_aarch64<br/>cp37-musllinux_ppc64le<br/>cp37-musllinux_s390x | | Python 3.7 | cp37-macosx_x86_64 | cp37-win_amd64<br/>cp37-win32 | cp37-manylinux_x86_64<br/>cp37-manylinux_i686<br/>cp37-musllinux_x86_64<br/>cp37-musllinux_i686 | cp37-manylinux_aarch64<br/>cp37-manylinux_ppc64le<br/>cp37-manylinux_s390x<br/>cp37-manylinux_armv7l<br/>cp37-musllinux_aarch64<br/>cp37-musllinux_ppc64le<br/>cp37-musllinux_s390x<br/>cp37-musllinux_armv7l |
| Python 3.8 | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64 | cp38-win_amd64<br/>cp38-win32 | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686 | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x | | Python 3.8 | cp38-macosx_x86_64<br/>cp38-macosx_universal2<br/>cp38-macosx_arm64 | cp38-win_amd64<br/>cp38-win32 | cp38-manylinux_x86_64<br/>cp38-manylinux_i686<br/>cp38-musllinux_x86_64<br/>cp38-musllinux_i686 | cp38-manylinux_aarch64<br/>cp38-manylinux_ppc64le<br/>cp38-manylinux_s390x<br/>cp38-manylinux_armv7l<br/>cp38-musllinux_aarch64<br/>cp38-musllinux_ppc64le<br/>cp38-musllinux_s390x<br/>cp38-musllinux_armv7l |
| Python 3.9 | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64 | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64 | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686 | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x | | Python 3.9 | cp39-macosx_x86_64<br/>cp39-macosx_universal2<br/>cp39-macosx_arm64 | cp39-win_amd64<br/>cp39-win32<br/>cp39-win_arm64 | cp39-manylinux_x86_64<br/>cp39-manylinux_i686<br/>cp39-musllinux_x86_64<br/>cp39-musllinux_i686 | cp39-manylinux_aarch64<br/>cp39-manylinux_ppc64le<br/>cp39-manylinux_s390x<br/>cp39-manylinux_armv7l<br/>cp39-musllinux_aarch64<br/>cp39-musllinux_ppc64le<br/>cp39-musllinux_s390x<br/>cp39-musllinux_armv7l |
| Python 3.10 | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x | | Python 3.10 | cp310-macosx_x86_64<br/>cp310-macosx_universal2<br/>cp310-macosx_arm64 | cp310-win_amd64<br/>cp310-win32<br/>cp310-win_arm64 | cp310-manylinux_x86_64<br/>cp310-manylinux_i686<br/>cp310-musllinux_x86_64<br/>cp310-musllinux_i686 | cp310-manylinux_aarch64<br/>cp310-manylinux_ppc64le<br/>cp310-manylinux_s390x<br/>cp310-manylinux_armv7l<br/>cp310-musllinux_aarch64<br/>cp310-musllinux_ppc64le<br/>cp310-musllinux_s390x<br/>cp310-musllinux_armv7l |
| Python 3.11 | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x | | Python 3.11 | cp311-macosx_x86_64<br/>cp311-macosx_universal2<br/>cp311-macosx_arm64 | cp311-win_amd64<br/>cp311-win32<br/>cp311-win_arm64 | cp311-manylinux_x86_64<br/>cp311-manylinux_i686<br/>cp311-musllinux_x86_64<br/>cp311-musllinux_i686 | cp311-manylinux_aarch64<br/>cp311-manylinux_ppc64le<br/>cp311-manylinux_s390x<br/>cp311-manylinux_armv7l<br/>cp311-musllinux_aarch64<br/>cp311-musllinux_ppc64le<br/>cp311-musllinux_s390x<br/>cp311-musllinux_armv7l |
| Python 3.12 | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-musllinux_aarch64<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x | | Python 3.12 | cp312-macosx_x86_64<br/>cp312-macosx_universal2<br/>cp312-macosx_arm64 | cp312-win_amd64<br/>cp312-win32<br/>cp312-win_arm64 | cp312-manylinux_x86_64<br/>cp312-manylinux_i686<br/>cp312-musllinux_x86_64<br/>cp312-musllinux_i686 | cp312-manylinux_aarch64<br/>cp312-manylinux_ppc64le<br/>cp312-manylinux_s390x<br/>cp312-musllinux_armv7l<br/>cp312-musllinux_ppc64le<br/>cp312-musllinux_s390x<br/>cp312-musllinux_armv7l |
| Python 3.13 | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x | | Python 3.13 | cp313-macosx_x86_64<br/>cp313-macosx_universal2<br/>cp313-macosx_arm64 | cp313-win_amd64<br/>cp313-win32<br/>cp313-win_arm64 | cp313-manylinux_x86_64<br/>cp313-manylinux_i686<br/>cp313-musllinux_x86_64<br/>cp313-musllinux_i686 | cp313-manylinux_aarch64<br/>cp313-manylinux_ppc64le<br/>cp313-manylinux_s390x<br/>cp313-manylinux_armv7l<br/>cp313-musllinux_aarch64<br/>cp313-musllinux_ppc64le<br/>cp313-musllinux_s390x<br/>cp313-musllinux_armv7l |
| PyPy3.7 v7.3 | pp37-macosx_x86_64 | pp37-win_amd64 | pp37-manylinux_x86_64<br/>pp37-manylinux_i686 | pp37-manylinux_aarch64 | | PyPy3.7 v7.3 | pp37-macosx_x86_64 | pp37-win_amd64 | pp37-manylinux_x86_64<br/>pp37-manylinux_i686 | pp37-manylinux_aarch64 |
| PyPy3.8 v7.3 | pp38-macosx_x86_64<br/>pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64<br/>pp38-manylinux_i686 | pp38-manylinux_aarch64 | | PyPy3.8 v7.3 | pp38-macosx_x86_64<br/>pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64<br/>pp38-manylinux_i686 | pp38-manylinux_aarch64 |
| PyPy3.9 v7.3 | pp39-macosx_x86_64<br/>pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64<br/>pp39-manylinux_i686 | pp39-manylinux_aarch64 | | PyPy3.9 v7.3 | pp39-macosx_x86_64<br/>pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64<br/>pp39-manylinux_i686 | pp39-manylinux_aarch64 |
| PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64 | pp310-win_amd64 | pp310-manylinux_x86_64<br/>pp310-manylinux_i686 | pp310-manylinux_aarch64 | | PyPy3.10 v7.3 | pp310-macosx_x86_64<br/>pp310-macosx_arm64 | pp310-win_amd64 | pp310-manylinux_x86_64<br/>pp310-manylinux_i686 | pp310-manylinux_aarch64 |
| PyPy3.11 v7.3 | pp311-macosx_x86_64<br/>pp311-macosx_arm64 | pp311-win_amd64 | pp311-manylinux_x86_64<br/>pp311-manylinux_i686 | pp311-manylinux_aarch64 |
The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel. The list of supported and currently selected build identifiers can also be retrieved by passing the `--print-build-identifiers` flag to cibuildwheel.
The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details). The format is `python_tag-platform_tag`, with tags similar to those in [PEP 425](https://www.python.org/dev/peps/pep-0425/#details).
For CPython, the minimally supported macOS version is 10.9; for PyPy 3.7, macOS 10.13 or higher is required. The lowest value you can set `MACOSX_DEPLOYMENT_TARGET` is as follows:
| Arch | Python version range | Minimum target |
|-------|----------------------|----------------|
| Intel | CPython 3.6-3.11 | 10.9 |
| Intel | CPython 3.12+ | 10.13 |
| AS | CPython or PyPy | 11 |
| Intel | PyPy 3.7-3.8 | 10.13 |
| Intel | PyPy 3.9+ | 10.15 |
If you set the value lower, cibuildwheel will cap it to the lowest supported value for each target as needed.
Windows arm64 platform support is experimental. Windows arm64 platform support is experimental.
@@ -412,52 +423,6 @@ See the [cibuildwheel 1 documentation](https://cibuildwheel.pypa.io/en/1.x/) for
} }
</style> </style>
### `CIBW_FREE_THREADED_SUPPORT` {: #free-threaded-support}
> Choose whether free-threaded variants should be built
[PEP 703](https://www.python.org/dev/peps/pep-0703) introduced variants of CPython that can be built without the Global Interpreter Lock (GIL).
Those variants are also known as free-threaded / no-gil.
Building for free-threaded variants is disabled by default.
Building can be enabled by setting this option to `true`. The free-threaded compatible wheels will be built in addition to the standard wheels.
This option doesn't support overrides.
If you need to enable/disable it per platform or python version, set this option to `true` and use [`CIBW_BUILD`](#build-skip)/[`CIBW_SKIP`](#build-skip) options to filter the builds.
The build identifiers for those variants have a `t` suffix in their `python_tag` (e.g. `cp313t-manylinux_x86_64`)
!!! note
This feature is experimental: [Whats New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
#### Examples
!!! tab examples "Environment variables"
```yaml
# Enable free-threaded support
CIBW_FREE_THREADED_SUPPORT: 1
# Skip building free-threaded compatible wheels on Windows
CIBW_FREE_THREADED_SUPPORT: 1
CIBW_SKIP: *t-win*
```
It is generally recommended to use `free-threaded-support` in a config file as you can statically declare that you
support free-threaded builds.
!!! tab examples "pyproject.toml"
```toml
[tool.cibuildwheel]
# Enable free-threaded support
free-threaded-support = true
# Skip building free-threaded compatible wheels on Windows
free-threaded-support = true
skip = "*t-win*"
```
### `CIBW_ARCHS` {: #archs} ### `CIBW_ARCHS` {: #archs}
> Change the architectures built on your machine by default. > Change the architectures built on your machine by default.
@@ -475,7 +440,7 @@ machine, provided the cross-compiling tools are installed.
Options: Options:
- Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x` - Linux: `x86_64` `i686` `aarch64` `ppc64le` `s390x` `armv7l`
- macOS: `x86_64` `arm64` `universal2` - macOS: `x86_64` `arm64` `universal2`
- Windows: `AMD64` `x86` `ARM64` - Windows: `AMD64` `x86` `ARM64`
- Pyodide: `wasm32` - Pyodide: `wasm32`
@@ -593,24 +558,53 @@ the package is compatible with all versions of Python that it can build.
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.6" CIBW_PROJECT_REQUIRES_PYTHON: ">=3.6"
``` ```
### `CIBW_PRERELEASE_PYTHONS` {: #prerelease-pythons} ### `CIBW_ENABLE` {: #enable}
> Enable building with pre-release versions of Python if available > Enable building with extra categories of selectors present.
This option lets you opt-in to non-default builds, like pre-releases and
free-threaded Python. These are not included by default to give a nice default
for new users, but can be added to the selectors available here. The allowed
values are:
- `cypython-prerelease`: Enables beta versions of Pythons if any are available
(May-July, approximately). For backward compatibility, `CIBW_PRERELEASE_PYTHONS`
is also supported until cibuildwheel 3.
- `cpython-freethreading`: [PEP 703](https://www.python.org/dev/peps/pep-0703)
introduced variants of CPython that can be built without the Global
Interpreter Lock (GIL). Those variants are also known as free-threaded /
no-gil. This will enable building these wheels while they are experimental.
The build identifiers for those variants have a `t` suffix in their
`python_tag` (e.g. `cp313t-manylinux_x86_64`). For backward compatibility,
`CIBW_FREE_THREADED_SUPPORT` is also supported until cibuildwheel 3.
- `pypy`: Enable PyPy. For backward compatibility, this is always enabled until
cibuildwheel 3 is released.
During the beta period, when new versions of Python are being tested,
cibuildwheel will often gain early support for beta releases. If you would
like to test wheel building with these versions, you can enable this flag.
!!! caution !!! caution
This option is provided for testing purposes only. It is not `cpython-prerelease` is provided for testing purposes only. It is not
recommended to distribute wheels built when `CIBW_PRERELEASE_PYTHONS` is recommended to distribute wheels built with beta releases, such as
set, such as uploading to PyPI. Please _do not_ upload these wheels to uploading to PyPI. Please _do not_ upload these wheels to PyPI, as they are
PyPI, as they are not guaranteed to work with the final Python release. not guaranteed to work with the final Python release. Once Python is ABI
Once Python is ABI stable and enters the release candidate phase, that stable and enters the release candidate phase, that version of Python will
version of Python will become available without this flag. become available without this flag.
Default: Off (0) if Python is available in beta phase. No effect otherwise. !!! note
Free threading is experimental: [Whats New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython)
Default: empty (`pypy` is always injected).
This option doesn't support overrides or platform specific variants; it is
intended as a way to acknowledge that a project is aware that these extra
selectors exist. If you need to enable/disable it per platform or python
version, set this option to `true` and use
[`CIBW_BUILD`](#build-skip)/[`CIBW_SKIP`](#build-skip) options to filter the
builds.
Unlike all other cibuildwheel options, the environment variable setting will
only add to the TOML config; you can't remove an enable by setting an empty or
partial list in environment variables; use `CIBW_SKIP` instead.
This option can also be set using the [command-line option](#command-line) `--prerelease-pythons`. This option is not available in the `pyproject.toml` config.
#### Examples #### Examples
@@ -618,9 +612,38 @@ This option can also be set using the [command-line option](#command-line) `--pr
```yaml ```yaml
# Include latest Python beta # Include latest Python beta
CIBW_PRERELEASE_PYTHONS: True CIBW_ENABLE: cpython-prerelease
# Include free-threaded support
CIBW_ENABLE: cpython-freethreading
# Include both
CIBW_ENABLE: cpython-prerelease cpython-freethreading
# Skip building free-threaded compatible wheels on Windows
CIBW_ENABLE: cpython-freethreading
CIBW_SKIP: *t-win*
``` ```
It is generally recommended to use `cpython-freethreading` in a config
file as you can statically declare that you support free-threaded builds.
!!! tab examples "pyproject.toml"
```toml
[tool.cibuildwheel]
# Enable free-threaded support
enable = ["cpython-freethreading"]
# Skip building free-threaded compatible wheels on Windows
enable = ["cpython-freethreading"]
skip = "*t-win*"
```
It is generally not recommended to use `cpython-prerelease` in a config file,
as it's intended for testing pre-releases for a 2-3 month period only.
### `CIBW_ALLOW_EMPTY` {: #allow-empty} ### `CIBW_ALLOW_EMPTY` {: #allow-empty}
> Suppress the error code if no wheels match the specified build identifiers > Suppress the error code if no wheels match the specified build identifiers
@@ -953,9 +976,9 @@ Platform-specific environment variables also available:<br/>
In configuration files, you can use a TOML array, and each line will be run sequentially - joined with `&&`. In configuration files, you can use a TOML array, and each line will be run sequentially - joined with `&&`.
Note that manylinux_2_24 builds occur inside a Debian9 docker, where Note that manylinux_2_24/manylinux_2_31 builds occur inside a debian derivative docker container, where
manylinux2010 and manylinux2014 builds occur inside a CentOS one. So for manylinux2010 and manylinux2014 builds occur inside a CentOS one. So for
`manylinux_2_24` the `CIBW_BEFORE_ALL_LINUX` command must use `apt-get -y` `manylinux_2_24`/`manylinux_2_31` the `CIBW_BEFORE_ALL_LINUX` command must use `apt-get -y`
instead. instead.
### `CIBW_BEFORE_BUILD` {: #before-build} ### `CIBW_BEFORE_BUILD` {: #before-build}
@@ -1110,6 +1133,7 @@ Platform-specific environment variables are also available:<br/>
delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} &&
pipx run abi3audit --strict --report {wheel} pipx run abi3audit --strict --report {wheel}
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: > CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >
copy {wheel} {dest_dir} &&
pipx run abi3audit --strict --report {wheel} pipx run abi3audit --strict --report {wheel}
``` ```
@@ -1148,7 +1172,10 @@ Platform-specific environment variables are also available:<br/>
"pipx run abi3audit --strict --report {wheel}", "pipx run abi3audit --strict --report {wheel}",
] ]
[tool.cibuildwheel.windows] [tool.cibuildwheel.windows]
repair-wheel-command = "pipx run abi3audit --strict --report {wheel}" repair-wheel-command = [
"copy {wheel} {dest_dir}",
"pipx run abi3audit --strict --report {wheel}",
]
``` ```
In configuration mode, you can use an inline array, and the items will be joined with `&&`. In configuration mode, you can use an inline array, and the items will be joined with `&&`.
@@ -1157,28 +1184,36 @@ Platform-specific environment variables are also available:<br/>
<div class="link-target" id="manylinux-image"></div> <div class="link-target" id="manylinux-image"></div>
### `CIBW_MANYLINUX_*_IMAGE`, `CIBW_MUSLLINUX_*_IMAGE` {: #linux-image} ### `CIBW_MANYLINUX_*_IMAGE`, `CIBW_MUSLLINUX_*_IMAGE` {: #linux-image}
> Specify alternative manylinux / musllinux container images
The available options are (default value): > Specify manylinux / musllinux container images
- `CIBW_MANYLINUX_X86_64_IMAGE` ([`quay.io/pypa/manylinux2014_x86_64`](https://quay.io/pypa/manylinux2014_x86_64)) The available options are:
- `CIBW_MANYLINUX_I686_IMAGE` ([`quay.io/pypa/manylinux2014_i686`](https://quay.io/pypa/manylinux2014_i686))
- `CIBW_MANYLINUX_PYPY_X86_64_IMAGE` ([`quay.io/pypa/manylinux2014_x86_64`](https://quay.io/pypa/manylinux2014_x86_64))
- `CIBW_MANYLINUX_AARCH64_IMAGE` ([`quay.io/pypa/manylinux2014_aarch64`](https://quay.io/pypa/manylinux2014_aarch64))
- `CIBW_MANYLINUX_PPC64LE_IMAGE` ([`quay.io/pypa/manylinux2014_ppc64le`](https://quay.io/pypa/manylinux2014_ppc64le))
- `CIBW_MANYLINUX_S390X_IMAGE` ([`quay.io/pypa/manylinux2014_s390x`](https://quay.io/pypa/manylinux2014_s390x))
- `CIBW_MANYLINUX_PYPY_AARCH64_IMAGE` ([`quay.io/pypa/manylinux2014_aarch64`](https://quay.io/pypa/manylinux2014_aarch64))
- `CIBW_MANYLINUX_PYPY_I686_IMAGE` ([`quay.io/pypa/manylinux2014_i686`](https://quay.io/pypa/manylinux2014_i686))
- `CIBW_MUSLLINUX_X86_64_IMAGE` ([`quay.io/pypa/musllinux_1_2_x86_64`](https://quay.io/pypa/musllinux_1_2_x86_64))
- `CIBW_MUSLLINUX_I686_IMAGE` ([`quay.io/pypa/musllinux_1_2_i686`](https://quay.io/pypa/musllinux_1_2_i686))
- `CIBW_MUSLLINUX_AARCH64_IMAGE` ([`quay.io/pypa/musllinux_1_2_aarch64`](https://quay.io/pypa/musllinux_1_2_aarch64))
- `CIBW_MUSLLINUX_PPC64LE_IMAGE` ([`quay.io/pypa/musllinux_1_2_ppc64le`](https://quay.io/pypa/musllinux_1_2_ppc64le))
- `CIBW_MUSLLINUX_S390X_IMAGE` ([`quay.io/pypa/musllinux_1_2_s390x`](https://quay.io/pypa/musllinux_1_2_s390x))
Set an alternative Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels. | Option | Default | Future default* |
|-----------------------------------|----------------------------------------------------------------|-----------------------------------------------------------------|
| CIBW_MANYLINUX_X86_64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_x86_64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
| CIBW_MANYLINUX_I686_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) | |
| CIBW_MANYLINUX_PYPY_X86_64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_x86_64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_x86_64) |
| CIBW_MANYLINUX_AARCH64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_aarch64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
| CIBW_MANYLINUX_PPC64LE_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_ppc64le) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_ppc64le) |
| CIBW_MANYLINUX_S390X_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_s390x) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_s390x) |
| CIBW_MANYLINUX_ARMV7L_IMAGE | [`manylinux_2_31`](https://quay.io/pypa/manylinux_2_31_armv7l) | |
| CIBW_MANYLINUX_PYPY_AARCH64_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_aarch64) | [`manylinux_2_28`](https://quay.io/pypa/manylinux_2_28_aarch64) |
| CIBW_MANYLINUX_PYPY_I686_IMAGE | [`manylinux2014`](https://quay.io/pypa/manylinux2014_i686) | |
| CIBW_MUSLLINUX_X86_64_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_x86_64) | |
| CIBW_MUSLLINUX_I686_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_i686) | |
| CIBW_MUSLLINUX_AARCH64_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_aarch64) | |
| CIBW_MUSLLINUX_PPC64LE_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_ppc64le) | |
| CIBW_MUSLLINUX_S390X_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_s390x) | |
| CIBW_MUSLLINUX_ARMV7L_IMAGE | [`musllinux_1_2`](https://quay.io/pypa/musllinux_1_2_armv7l) | |
For `CIBW_MANYLINUX_*_IMAGE`, the value of this option can either be set to `manylinux1`, `manylinux2010`, `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name. For PyPy, the `manylinux1` image is not available. For architectures other <small>* The default is scheduled to change in a cibuildwheel release on or after 6th May 2025 - if you don't want the new default, you should set the value to `manylinux2014`.</small>
Set the Docker image to be used for building [manylinux / musllinux](https://github.com/pypa/manylinux) wheels.
For `CIBW_MANYLINUX_*_IMAGE`, except `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux1`, `manylinux2010`, `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` to use a pinned version of the [official manylinux images](https://github.com/pypa/manylinux). Alternatively, set these options to any other valid Docker image name. For PyPy, the `manylinux1` image is not available. For architectures other
than x86 (x86\_64 and i686) `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` must be used, because the first version of the manylinux specification that supports additional architectures is `manylinux2014`. `manylinux_2_28` is not supported for `i686` architecture. than x86 (x86\_64 and i686) `manylinux2014`, `manylinux_2_24` or `manylinux_2_28` must be used, because the first version of the manylinux specification that supports additional architectures is `manylinux2014`. `manylinux_2_28` is not supported for `i686` architecture.
For `CIBW_MANYLINUX_ARMV7L_IMAGE`, the value of this option can either be set to `manylinux_2_31` or a custom image. Support is experimental for now. The `manylinux_2_31` value is only available for `armv7`.
For `CIBW_MUSLLINUX_*_IMAGE`, the value of this option can either be set to `musllinux_1_1` or `musllinux_1_2` to use a pinned version of the [official musllinux images](https://github.com/pypa/musllinux). Alternatively, set these options to any other valid Docker image name. For `CIBW_MUSLLINUX_*_IMAGE`, the value of this option can either be set to `musllinux_1_1` or `musllinux_1_2` to use a pinned version of the [official musllinux images](https://github.com/pypa/musllinux). Alternatively, set these options to any other valid Docker image name.
@@ -1584,6 +1619,40 @@ Platform-specific environment variables are also available:<br/>
In configuration files, you can use an inline array, and the items will be joined with a comma. In configuration files, you can use an inline array, and the items will be joined with a comma.
### `CIBW_TEST_GROUPS` {: #test-groups}
> Specify test dependencies from your project's `dependency-groups`
List of
[dependency-groups](https://peps.python.org/pep-0735)
that should be included when installing the wheel prior to running the
tests. This can be used to avoid having to redefine test dependencies in
`CIBW_TEST_REQUIRES` if they are already defined in `pyproject.toml`.
Platform-specific environment variables are also available:<br/>
`CIBW_TEST_GROUPS_MACOS` | `CIBW_TEST_GROUPS_WINDOWS` | `CIBW_TEST_GROUPS_LINUX` | `CIBW_TEST_GROUPS_PYODIDE`
#### Examples
!!! tab examples "Environment variables"
```yaml
# Will cause the wheel to be installed with these groups of dependencies
CIBW_TEST_GROUPS: "test qt"
```
Separate multiple items with a space.
!!! tab examples "pyproject.toml"
```toml
[tool.cibuildwheel]
# Will cause the wheel to be installed with these groups of dependencies
test-groups = ["test", "qt"]
```
In configuration files, you can use an inline array, and the items will be joined with a space.
### `CIBW_TEST_SKIP` {: #test-skip} ### `CIBW_TEST_SKIP` {: #test-skip}
> Skip running tests on some builds > Skip running tests on some builds
@@ -1599,7 +1668,7 @@ This option is not supported in the overrides section in `pyproject.toml`.
```yaml ```yaml
# Will avoid testing on emulated architectures # Will avoid testing on emulated architectures
CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x}" CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x,armv7l}"
# Skip trying to test arm64 builds on Intel Macs # Skip trying to test arm64 builds on Intel Macs
CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64" CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64"
@@ -1610,7 +1679,7 @@ This option is not supported in the overrides section in `pyproject.toml`.
```toml ```toml
[tool.cibuildwheel] [tool.cibuildwheel]
# Will avoid testing on emulated architectures # Will avoid testing on emulated architectures
test-skip = "*-*linux_{aarch64,ppc64le,s390x}" test-skip = "*-*linux_{aarch64,ppc64le,s390x,armv7l}"
# Skip trying to test arm64 builds on Intel Macs # Skip trying to test arm64 builds on Intel Macs
test-skip = "*-macosx_arm64 *-macosx_universal2:arm64" test-skip = "*-macosx_arm64 *-macosx_universal2:arm64"
+6 -6
View File
@@ -154,14 +154,14 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-15-intel is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build wheels - name: Build wheels
run: pipx run cibuildwheel==2.20.0 run: pipx run cibuildwheel==2.23.4
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
@@ -188,8 +188,8 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-15-intel is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -198,7 +198,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
- name: Install cibuildwheel - name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.20.0 run: python -m pip install cibuildwheel==2.23.4
- name: Build wheels - name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse run: python -m cibuildwheel --output-dir wheelhouse
+24 -24
View File
@@ -18,8 +18,8 @@ title: Working examples
| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. | | [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. | | [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS | | [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python |
| [Triton][] | ![github icon][] | ![linux icon][] | Self hosted runners | | [Triton][] | ![github icon][] | ![linux icon][] | Self hosted runners |
| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python |
| [uvloop][] | ![github icon][] | ![apple icon][] ![linux icon][] | Ultra fast asyncio event loop. | | [uvloop][] | ![github icon][] | ![apple icon][] ![linux icon][] | Ultra fast asyncio event loop. |
| [psutil][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Cross-platform lib for process and system monitoring in Python | | [psutil][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Cross-platform lib for process and system monitoring in Python |
| [Google Benchmark][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A microbenchmark support library | | [Google Benchmark][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A microbenchmark support library |
@@ -32,18 +32,18 @@ title: Working examples
| [twisted-iocpsupport][] | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. | | [twisted-iocpsupport][] | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. |
| [PyOxidizer][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool | | [PyOxidizer][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool |
| [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. | | [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. |
| [websockets][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module. |
| [pedalboard][] | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | A Python library for working with audio data and audio plugins by wrapping the [JUCE](https://github.com/juce-framework/JUCE/) C++ framework. Uses cibuildwheel to deploy on as many operating systems and Python versions as possible with only one dependency (any NumPy). | | [pedalboard][] | ![github icon][] | ![windows icon][] ![linux icon][] ![apple icon][] | A Python library for working with audio data and audio plugins by wrapping the [JUCE](https://github.com/juce-framework/JUCE/) C++ framework. Uses cibuildwheel to deploy on as many operating systems and Python versions as possible with only one dependency (any NumPy). |
| [websockets][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module. |
| [River][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python | | [River][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python |
| [UltraJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings | | [UltraJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings |
| [OpenSpiel][] | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. |
| [aiortc][] | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. | | [aiortc][] | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. |
| [OpenSpiel][] | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. |
| [Dependency Injector][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI | | [Dependency Injector][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Dependency injection framework for Python, uses Windows TravisCI |
| [pyzmq][] | ![github icon][] ![circleci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython on CPython and CFFI on PyPy. ARM wheels for linux are built natively on CircleCI. | | [pyzmq][] | ![github icon][] ![circleci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython on CPython and CFFI on PyPy. ARM wheels for linux are built natively on CircleCI. |
| [Implicit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels | | [Implicit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes GPU support for linux wheels |
| [vispy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Main repository for Vispy |
| [tinyobjloader][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Tiny but powerful single file wavefront obj loader |
| [CTranslate2][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. | | [CTranslate2][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. |
| [tinyobjloader][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Tiny but powerful single file wavefront obj loader |
| [vispy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Main repository for Vispy |
| [coverage.py][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The coverage tool for Python | | [coverage.py][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The coverage tool for Python |
| [PyCryptodome][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A self-contained cryptographic library for Python | | [PyCryptodome][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A self-contained cryptographic library for Python |
| [Line Profiler][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Line-by-line profiling for Python | | [Line Profiler][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Line-by-line profiling for Python |
@@ -78,33 +78,33 @@ title: Working examples
| [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] ![appveyor icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson | | [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] ![appveyor icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson |
| [pybind11 python_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system | | [pybind11 python_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
| [python-snappy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library | | [python-snappy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library |
| [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
| [sourmash][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. | | [sourmash][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
| [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
| [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing | | [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
| [matrixprofile][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
| [jq.py][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq | | [jq.py][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
| [matrixprofile][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
| [iminuit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 | | [iminuit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
| [Tokenizer][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support | | [Tokenizer][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
| [keyvi][] | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime | | [keyvi][] | ![github icon][] | ![linux icon][] ![apple icon][] | FST based key value index highly optimized for size and lookup performance, utilizes ccache action for improved runtime |
| [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
| [pillow-heif][] | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. | | [pillow-heif][] | ![github icon][] ![cirrusci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Bindings to libheif library with third party dependencies. Fully automated CI for tests and publishing including Apple Silicon builds. |
| [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python |
| [TgCrypto][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. | | [TgCrypto][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. |
| [iDynTree][] | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 | | [iDynTree][] | ![github icon][] | ![linux icon][] | Uses manylinux_2_24 |
| [streaming-form-data][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython | | [streaming-form-data][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Streaming parser for multipart/form-data written in Cython |
| [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. | | [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. |
| [boost-histogram][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
| [power-grid-model][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis | | [power-grid-model][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis |
| [Python-WebRTC][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 | | [boost-histogram][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. |
| [pybase64][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python | | [pybase64][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
| [Python-WebRTC][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | a Python extension that provides bindings to WebRTC M92 |
| [Confluent client for Kafka][] | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
| [Imagecodecs (fork)][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. | | [Imagecodecs (fork)][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
| [fathon][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms | | [fathon][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
| [Arbor][] | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries. |
| [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 | | [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
| [Arbor][] | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries. |
| [polaroid][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. | | [polaroid][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
| [clang-format][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. | | [clang-format][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
| [etebase-py][] | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. | | [etebase-py][] | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
| [cf-units][] | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions | | [cf-units][] | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions |
| [Confluent client for Kafka][] | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
| [ninja][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. | | [ninja][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
| [numpythia][] | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy | | [numpythia][] | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
| [pyjet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy | | [pyjet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
@@ -128,8 +128,8 @@ title: Working examples
[Prophet]: https://github.com/facebook/prophet [Prophet]: https://github.com/facebook/prophet
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels [MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
[Kivy]: https://github.com/kivy/kivy [Kivy]: https://github.com/kivy/kivy
[MemRay]: https://github.com/bloomberg/memray
[Triton]: https://github.com/openai/triton [Triton]: https://github.com/openai/triton
[MemRay]: https://github.com/bloomberg/memray
[uvloop]: https://github.com/MagicStack/uvloop [uvloop]: https://github.com/MagicStack/uvloop
[psutil]: https://github.com/giampaolo/psutil [psutil]: https://github.com/giampaolo/psutil
[Google Benchmark]: https://github.com/google/benchmark [Google Benchmark]: https://github.com/google/benchmark
@@ -142,18 +142,18 @@ title: Working examples
[twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport [twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport
[PyOxidizer]: https://github.com/indygreg/PyOxidizer [PyOxidizer]: https://github.com/indygreg/PyOxidizer
[cvxpy]: https://github.com/cvxpy/cvxpy [cvxpy]: https://github.com/cvxpy/cvxpy
[websockets]: https://github.com/python-websockets/websockets
[pedalboard]: https://github.com/spotify/pedalboard [pedalboard]: https://github.com/spotify/pedalboard
[websockets]: https://github.com/python-websockets/websockets
[River]: https://github.com/online-ml/river [River]: https://github.com/online-ml/river
[UltraJSON]: https://github.com/ultrajson/ultrajson [UltraJSON]: https://github.com/ultrajson/ultrajson
[OpenSpiel]: https://github.com/google-deepmind/open_spiel
[aiortc]: https://github.com/aiortc/aiortc [aiortc]: https://github.com/aiortc/aiortc
[OpenSpiel]: https://github.com/google-deepmind/open_spiel
[Dependency Injector]: https://github.com/ets-labs/python-dependency-injector [Dependency Injector]: https://github.com/ets-labs/python-dependency-injector
[pyzmq]: https://github.com/zeromq/pyzmq [pyzmq]: https://github.com/zeromq/pyzmq
[Implicit]: https://github.com/benfred/implicit [Implicit]: https://github.com/benfred/implicit
[vispy]: https://github.com/vispy/vispy
[tinyobjloader]: https://github.com/tinyobjloader/tinyobjloader
[CTranslate2]: https://github.com/OpenNMT/CTranslate2 [CTranslate2]: https://github.com/OpenNMT/CTranslate2
[tinyobjloader]: https://github.com/tinyobjloader/tinyobjloader
[vispy]: https://github.com/vispy/vispy
[coverage.py]: https://github.com/nedbat/coveragepy [coverage.py]: https://github.com/nedbat/coveragepy
[PyCryptodome]: https://github.com/Legrandin/pycryptodome [PyCryptodome]: https://github.com/Legrandin/pycryptodome
[Line Profiler]: https://github.com/pyutils/line_profiler [Line Profiler]: https://github.com/pyutils/line_profiler
@@ -188,33 +188,33 @@ title: Working examples
[python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson [python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson
[pybind11 python_example]: https://github.com/pybind/python_example [pybind11 python_example]: https://github.com/pybind/python_example
[python-snappy]: https://github.com/intake/python-snappy [python-snappy]: https://github.com/intake/python-snappy
[abess]: https://github.com/abess-team/abess
[sourmash]: https://github.com/sourmash-bio/sourmash [sourmash]: https://github.com/sourmash-bio/sourmash
[abess]: https://github.com/abess-team/abess
[cyvcf2]: https://github.com/brentp/cyvcf2 [cyvcf2]: https://github.com/brentp/cyvcf2
[matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
[jq.py]: https://github.com/mwilliamson/jq.py [jq.py]: https://github.com/mwilliamson/jq.py
[matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
[iminuit]: https://github.com/scikit-hep/iminuit [iminuit]: https://github.com/scikit-hep/iminuit
[Tokenizer]: https://github.com/OpenNMT/Tokenizer [Tokenizer]: https://github.com/OpenNMT/Tokenizer
[keyvi]: https://github.com/KeyviDev/keyvi [keyvi]: https://github.com/KeyviDev/keyvi
[PyGLM]: https://github.com/Zuzu-Typ/PyGLM
[pillow-heif]: https://github.com/bigcat88/pillow_heif [pillow-heif]: https://github.com/bigcat88/pillow_heif
[PyGLM]: https://github.com/Zuzu-Typ/PyGLM
[TgCrypto]: https://github.com/pyrogram/tgcrypto [TgCrypto]: https://github.com/pyrogram/tgcrypto
[iDynTree]: https://github.com/robotology/idyntree [iDynTree]: https://github.com/robotology/idyntree
[streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data [streaming-form-data]: https://github.com/siddhantgoel/streaming-form-data
[bx-python]: https://github.com/bxlab/bx-python [bx-python]: https://github.com/bxlab/bx-python
[boost-histogram]: https://github.com/scikit-hep/boost-histogram
[power-grid-model]: https://github.com/PowerGridModel/power-grid-model [power-grid-model]: https://github.com/PowerGridModel/power-grid-model
[Python-WebRTC]: https://github.com/MarshalX/python-webrtc [boost-histogram]: https://github.com/scikit-hep/boost-histogram
[pybase64]: https://github.com/mayeut/pybase64 [pybase64]: https://github.com/mayeut/pybase64
[Python-WebRTC]: https://github.com/MarshalX/python-webrtc
[Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
[Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
[fathon]: https://github.com/stfbnc/fathon [fathon]: https://github.com/stfbnc/fathon
[Arbor]: https://github.com/arbor-sim/arbor
[pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example [pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
[Arbor]: https://github.com/arbor-sim/arbor
[polaroid]: https://github.com/daggy1234/polaroid [polaroid]: https://github.com/daggy1234/polaroid
[clang-format]: https://github.com/ssciwr/clang-format-wheel [clang-format]: https://github.com/ssciwr/clang-format-wheel
[etebase-py]: https://github.com/etesync/etebase-py [etebase-py]: https://github.com/etesync/etebase-py
[cf-units]: https://github.com/SciTools/cf-units [cf-units]: https://github.com/SciTools/cf-units
[Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
[ninja]: https://github.com/scikit-build/ninja-python-distributions [ninja]: https://github.com/scikit-build/ninja-python-distributions
[numpythia]: https://github.com/scikit-hep/numpythia [numpythia]: https://github.com/scikit-hep/numpythia
[pyjet]: https://github.com/scikit-hep/pyjet [pyjet]: https://github.com/scikit-hep/pyjet
+1 -1
View File
@@ -9,7 +9,7 @@ environment:
stack: python 3.12 stack: python 3.12
install: python -m pip install cibuildwheel==2.20.0 install: python -m pip install cibuildwheel==2.23.4
build_script: python -m cibuildwheel --output-dir wheelhouse build_script: python -m cibuildwheel --output-dir wheelhouse
+4 -4
View File
@@ -6,7 +6,7 @@ jobs:
- bash: | - bash: |
set -o errexit set -o errexit
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip3 install cibuildwheel==2.20.0 pip3 install cibuildwheel==2.23.4
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
@@ -14,13 +14,13 @@ jobs:
inputs: {pathtoPublish: 'wheelhouse'} inputs: {pathtoPublish: 'wheelhouse'}
- job: macos - job: macos
pool: {vmImage: 'macOS-12'} pool: {vmImage: 'macOS-13'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
- bash: | - bash: |
set -o errexit set -o errexit
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
python3 -m pip install cibuildwheel==2.20.0 python3 -m pip install cibuildwheel==2.23.4
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
@@ -34,7 +34,7 @@ jobs:
- bash: | - bash: |
set -o errexit set -o errexit
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install cibuildwheel==2.20.0 pip install cibuildwheel==2.23.4
displayName: Install dependencies displayName: Install dependencies
- bash: cibuildwheel --output-dir wheelhouse . - bash: cibuildwheel --output-dir wheelhouse .
displayName: Build wheels displayName: Build wheels
+3 -3
View File
@@ -11,7 +11,7 @@ jobs:
- run: - run:
name: Build the Linux wheels. name: Build the Linux wheels.
command: | command: |
python3 -m pip install --user cibuildwheel==2.20.0 python3 -m pip install --user cibuildwheel==2.23.4
cibuildwheel --output-dir wheelhouse cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
@@ -28,7 +28,7 @@ jobs:
- run: - run:
name: Build the Linux aarch64 wheels. name: Build the Linux aarch64 wheels.
command: | command: |
python3 -m pip install --user cibuildwheel==2.20.0 python3 -m pip install --user cibuildwheel==2.23.4
python3 -m cibuildwheel --output-dir wheelhouse python3 -m cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
@@ -44,7 +44,7 @@ jobs:
name: Build the OS X wheels. name: Build the OS X wheels.
command: | command: |
sudo softwareupdate --install-rosetta --agree-to-license # for python<=3.8 or x86_64/universal2 tests sudo softwareupdate --install-rosetta --agree-to-license # for python<=3.8 or x86_64/universal2 tests
pip3 install cibuildwheel==2.20.0 pip3 install cibuildwheel==2.23.4
cibuildwheel --output-dir wheelhouse cibuildwheel --output-dir wheelhouse
- store_artifacts: - store_artifacts:
path: wheelhouse/ path: wheelhouse/
+2 -2
View File
@@ -1,6 +1,6 @@
build_and_store_wheels: &BUILD_AND_STORE_WHEELS build_and_store_wheels: &BUILD_AND_STORE_WHEELS
install_cibuildwheel_script: install_cibuildwheel_script:
- python -m pip install cibuildwheel==2.20.0 - python -m pip install cibuildwheel==2.23.4
run_cibuildwheel_script: run_cibuildwheel_script:
- cibuildwheel - cibuildwheel
wheels_artifacts: wheels_artifacts:
@@ -10,7 +10,7 @@ build_and_store_wheels: &BUILD_AND_STORE_WHEELS
macos_task: macos_task:
name: Build macOS x86_64 and arm64 wheels. name: Build macOS x86_64 and arm64 wheels.
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode image: ghcr.io/cirruslabs/macos-runner:sonoma
env: env:
VENV_ROOT: ${HOME}/venv-cibuildwheel VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH} PATH: ${VENV_ROOT}/bin:${PATH}
+2 -2
View File
@@ -1,6 +1,6 @@
build_and_store_wheels: &BUILD_AND_STORE_WHEELS build_and_store_wheels: &BUILD_AND_STORE_WHEELS
install_cibuildwheel_script: install_cibuildwheel_script:
- python -m pip install cibuildwheel==2.20.0 - python -m pip install cibuildwheel==2.23.4
run_cibuildwheel_script: run_cibuildwheel_script:
- cibuildwheel - cibuildwheel
wheels_artifacts: wheels_artifacts:
@@ -60,7 +60,7 @@ windows_x86_task:
macos_arm64_task: macos_arm64_task:
name: Build macOS arm64 wheels. name: Build macOS arm64 wheels.
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode image: ghcr.io/cirruslabs/macos-runner:sonoma
env: env:
VENV_ROOT: ${HOME}/venv-cibuildwheel VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH} PATH: ${VENV_ROOT}/bin:${PATH}
+3 -3
View File
@@ -16,14 +16,14 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-15-intel is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.20.0 uses: pypa/cibuildwheel@v2.23.4
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
+3 -3
View File
@@ -8,14 +8,14 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-15-intel is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.20.0 uses: pypa/cibuildwheel@v2.23.4
# env: # env:
# CIBW_SOME_OPTION: value # CIBW_SOME_OPTION: value
# ... # ...
+11 -7
View File
@@ -8,24 +8,28 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
# macos-13 is an intel runner, macos-14 is apple silicon # macos-15-intel is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, windows-latest, macos-13, macos-14] os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up QEMU - name: Set up QEMU
if: runner.os == 'Linux' if: runner.os == 'Linux' && runner.arch == 'X64'
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
with: with:
platforms: all platforms: all
- name: Build wheels - name: Build wheels
uses: pypa/cibuildwheel@v2.20.0 uses: pypa/cibuildwheel@v2.23.4
env: env:
# configure cibuildwheel to build native archs ('auto'), and some # configure cibuildwheel on Linux to build native archs ('auto'),
# emulated ones # and to split the remaining architectures between the x86_64 and
CIBW_ARCHS_LINUX: auto aarch64 ppc64le s390x # ARM runners
# armv7l can be built without QEMU on GitHub Actions ARM runners but that's
# not the case on all ARM64 hardware hence 'auto armv7l' for native archs
# on the GHA ARM64 runner
CIBW_ARCHS_LINUX: ${{ runner.arch == 'X64' && 'auto ppc64le s390x' || 'auto armv7l' }}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
+4 -4
View File
@@ -12,7 +12,7 @@ linux:
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
script: script:
- curl -sSL https://get.docker.com/ | sh - curl -sSL https://get.docker.com/ | sh
- python -m pip install cibuildwheel==2.20.0 - python -m pip install cibuildwheel==2.23.4
- cibuildwheel --output-dir wheelhouse - cibuildwheel --output-dir wheelhouse
artifacts: artifacts:
paths: paths:
@@ -23,7 +23,7 @@ windows:
before_script: before_script:
- choco install python -y --version 3.12.4 - choco install python -y --version 3.12.4
- choco install git.install -y - choco install git.install -y
- py -m pip install cibuildwheel==2.20.0 - py -m pip install cibuildwheel==2.23.4
script: script:
- py -m cibuildwheel --output-dir wheelhouse --platform windows - py -m cibuildwheel --output-dir wheelhouse --platform windows
artifacts: artifacts:
@@ -33,9 +33,9 @@ windows:
- saas-windows-medium-amd64 - saas-windows-medium-amd64
macos: macos:
image: macos-14-xcode-15 image: macos-latest-xcode-15
before_script: before_script:
- python3 -m pip install cibuildwheel==2.20.0 - python3 -m pip install cibuildwheel==2.23.4
script: script:
- python3 -m cibuildwheel --output-dir wheelhouse - python3 -m cibuildwheel --output-dir wheelhouse
artifacts: artifacts:
+1 -1
View File
@@ -14,7 +14,7 @@ linux:
- curl -sSL https://get.docker.com/ | sh - curl -sSL https://get.docker.com/ | sh
# Warning: This is extremely slow, be careful with how many wheels you build # Warning: This is extremely slow, be careful with how many wheels you build
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- python -m pip install cibuildwheel==2.20.0 - python -m pip install cibuildwheel==2.23.4
# Assuming your CI runner's default architecture is x86_64... # Assuming your CI runner's default architecture is x86_64...
- cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64 - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64
artifacts: artifacts:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- ln -s /c/Python312/python.exe /c/Python312/python3.exe - ln -s /c/Python312/python.exe /c/Python312/python3.exe
install: install:
- python3 -m pip install cibuildwheel==2.20.0 - python3 -m pip install cibuildwheel==2.23.4
script: script:
# build the wheels, put them into './dist' # build the wheels, put them into './dist'
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- ln -s /c/Python312/python.exe /c/Python312/python3.exe - ln -s /c/Python312/python.exe /c/Python312/python3.exe
install: install:
- python3 -m pip install cibuildwheel==2.20.0 - python3 -m pip install cibuildwheel==2.23.4
script: script:
# build the wheels, put them into './wheelhouse' # build the wheels, put them into './wheelhouse'
+2 -2
View File
@@ -52,7 +52,7 @@ jobs:
- stage: deploy - stage: deploy
name: Build and deploy Linux wheels name: Build and deploy Linux wheels
services: docker services: docker
install: python3 -m pip install cibuildwheel==2.20.0 twine install: python3 -m pip install cibuildwheel==2.23.4 twine
script: python3 -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
# Deploy on windows # Deploy on windows
@@ -60,7 +60,7 @@ jobs:
name: Build and deploy Windows wheels name: Build and deploy Windows wheels
os: windows os: windows
language: shell language: shell
install: python3 -m pip install cibuildwheel==2.20.0 twine install: python3 -m pip install cibuildwheel==2.23.4 twine
script: python3 -m cibuildwheel --output-dir wheelhouse script: python3 -m cibuildwheel --output-dir wheelhouse
after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl
+10 -6
View File
@@ -15,18 +15,22 @@ nox.options.default_venv_backend = "uv|virtualenv"
DIR = Path(__file__).parent.resolve() DIR = Path(__file__).parent.resolve()
def install_and_run(session: nox.Session, script: str, *args: str, **kwargs: Any) -> str | None: def install_and_run(session: nox.Session, script: str, *args: str, **kwargs: Any) -> Any:
deps = nox.project.load_toml(script)["dependencies"] deps = nox.project.load_toml(script)["dependencies"]
session.install(*deps) session.install(*deps)
return session.run("python", script, *args, **kwargs) return session.run("python", script, *args, **kwargs)
def dep_group(group: str) -> list[str]:
return nox.project.load_toml("pyproject.toml")["dependency-groups"][group] # type: ignore[no-any-return]
@nox.session @nox.session
def tests(session: nox.Session) -> None: def tests(session: nox.Session) -> None:
""" """
Run the unit and regular tests. Run the unit and regular tests.
""" """
session.install("-e.[test]") session.install("-e.", *dep_group("test"))
if session.posargs: if session.posargs:
session.run("pytest", *session.posargs) session.run("pytest", *session.posargs)
else: else:
@@ -102,10 +106,10 @@ def update_constraints(session: nox.Session) -> None:
pyodides = build_platforms["pyodide"]["python_configurations"] pyodides = build_platforms["pyodide"]["python_configurations"]
for pyodide in pyodides: for pyodide in pyodides:
python_version = ".".join(pyodide["version"].split(".")[:2]) python_version = ".".join(pyodide["version"].split(".")[:2])
pyodide_version = pyodide["pyodide_version"] pyodide_build_version = pyodide["pyodide_build_version"]
output_file = resources / f"constraints-pyodide{python_version.replace('.', '')}.txt" output_file = resources / f"constraints-pyodide{python_version.replace('.', '')}.txt"
tmp_file = Path(session.create_tmp()) / "constraints-pyodide.in" tmp_file = Path(session.create_tmp()) / "constraints-pyodide.in"
tmp_file.write_text(f"pip\nbuild[virtualenv]\npyodide-build=={pyodide_version}") tmp_file.write_text(f"pip\nbuild[virtualenv]\npyodide-build=={pyodide_build_version}")
session.run( session.run(
"uv", "uv",
"pip", "pip",
@@ -123,7 +127,7 @@ def update_pins(session: nox.Session) -> None:
""" """
Update the python, docker and virtualenv pins version inplace. Update the python, docker and virtualenv pins version inplace.
""" """
session.install("-e.[bin]") session.install("-e.", *dep_group("bin"))
session.run("python", "bin/update_pythons.py", "--force") session.run("python", "bin/update_pythons.py", "--force")
session.run("python", "bin/update_docker.py") session.run("python", "bin/update_docker.py")
session.run("python", "bin/update_virtualenv.py", "--force") session.run("python", "bin/update_virtualenv.py", "--force")
@@ -166,7 +170,7 @@ def docs(session: nox.Session) -> None:
""" """
Build the docs. Will serve unless --non-interactive Build the docs. Will serve unless --non-interactive
""" """
session.install("-e.[docs]") session.install("-e.", *dep_group("docs"))
session.run("mkdocs", "serve" if session.interactive else "build", "--strict", *session.posargs) session.run("mkdocs", "serve" if session.interactive else "build", "--strict", *session.posargs)
+44 -47
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "cibuildwheel" name = "cibuildwheel"
version = "2.20.0" version = "2.23.4"
description = "Build Python wheels on CI with minimal configuration." description = "Build Python wheels on CI with minimal configuration."
readme = "README.md" readme = "README.md"
license = "BSD-2-Clause" license = "BSD-2-Clause"
@@ -35,6 +35,7 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"Topic :: Software Development :: Build Tools", "Topic :: Software Development :: Build Tools",
] ]
@@ -42,6 +43,7 @@ dependencies = [
"bashlex!=0.13", "bashlex!=0.13",
"bracex", "bracex",
"certifi", "certifi",
"dependency-groups>=1.2",
"filelock", "filelock",
"packaging>=20.9", "packaging>=20.9",
"platformdirs", "platformdirs",
@@ -50,35 +52,6 @@ dependencies = [
] ]
[project.optional-dependencies] [project.optional-dependencies]
bin = [
"click",
"packaging>=21.0",
"pip-tools",
"pygithub",
"pyyaml",
"requests",
"rich>=9.6",
]
dev = [
"cibuildwheel[test,bin]",
]
docs = [
"jinja2>=3.1.2",
"mkdocs-include-markdown-plugin==2.8.0",
"mkdocs-macros-plugin",
"mkdocs==1.3.1",
"pymdown-extensions",
]
test = [
"build",
"jinja2",
"pytest-timeout",
"pytest-xdist",
"pytest>=6",
"setuptools",
"tomli_w",
"validate-pyproject",
]
uv = ["uv"] uv = ["uv"]
[project.scripts] [project.scripts]
@@ -92,6 +65,41 @@ Changelog = "https://github.com/pypa/cibuildwheel#changelog"
Documentation = "https://cibuildwheel.pypa.io" Documentation = "https://cibuildwheel.pypa.io"
Homepage = "https://github.com/pypa/cibuildwheel" Homepage = "https://github.com/pypa/cibuildwheel"
[dependency-groups]
bin = [
"click",
"packaging>=21.0",
"pip-tools",
"pygithub",
"pyyaml",
"requests",
"rich>=9.6",
]
docs = [
"jinja2>=3.1.2",
"mkdocs-include-markdown-plugin==6.2.2",
"mkdocs-macros-plugin",
"mkdocs==1.6.1",
"pymdown-extensions",
]
test = [
"build",
"jinja2",
"pytest-timeout",
"pytest-xdist",
"pytest-rerunfailures!=16.0",
"pytest>=6",
"setuptools",
"tomli_w",
"validate-pyproject",
]
dev = [
{include-group = "bin"},
{include-group = "docs"},
{include-group = "test"},
]
[tool.pytest.ini_options] [tool.pytest.ini_options]
minversion = "6.0" minversion = "6.0"
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
@@ -112,33 +120,20 @@ files = [
] ]
warn_unused_configs = true warn_unused_configs = true
warn_redundant_casts = true strict = true
no_implicit_reexport = true disallow_untyped_defs = false
strict_equality = true
warn_unused_ignores = true
check_untyped_defs = true
disallow_subclassing_any = true
disallow_any_generics = true
warn_return_any = true
no_implicit_optional = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true warn_unreachable = false
[[tool.mypy.overrides]] [[tool.mypy.overrides]]
module = "cibuildwheel.*" module = "cibuildwheel.*"
disallow_untyped_defs = true disallow_untyped_defs = true
disallow_untyped_calls = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
[[tool.mypy.overrides]] [[tool.mypy.overrides]]
module = [ module = [
"setuptools",
"setuptools._distutils", # needed even if only directly import setuptools._distutils.util "setuptools._distutils", # needed even if only directly import setuptools._distutils.util
"setuptools._distutils.util", "setuptools._distutils.util",
"pytest", # ignored in pre-commit to speed up check
"bashlex", "bashlex",
"bashlex.*", "bashlex.*",
"importlib_resources", "importlib_resources",
@@ -213,7 +208,6 @@ ignore = [
"PLR", # Design related pylint codes "PLR", # Design related pylint codes
"E501", # Line too long "E501", # Line too long
"RET504", "RET505", "RET508", # else after control flow "RET504", "RET505", "RET508", # else after control flow
"PT004", # Rename suggested for returnless fixtures
"PT007", # False positive "PT007", # False positive
"PYI025", # Set as AbstractSet "PYI025", # Set as AbstractSet
"ISC001", # Conflicts with formatter "ISC001", # Conflicts with formatter
@@ -239,3 +233,6 @@ flake8-unused-arguments.ignore-variadic-names = true
[tool.repo-review] [tool.repo-review]
ignore = ["PC170", "PP303"] ignore = ["PC170", "PP303"]
[tool.check-wheel-contents]
ignore = ["W002"] # requirements-*.txt are allowed to be duplicates of one another
-1
View File
@@ -1 +0,0 @@
-e .[dev,docs]
+1 -1
View File
@@ -11,7 +11,7 @@ from cibuildwheel.util import detect_ci_provider, find_uv
from .utils import EMULATED_ARCHS, platform from .utils import EMULATED_ARCHS, platform
def pytest_addoption(parser) -> None: def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption( parser.addoption(
"--run-emulation", "--run-emulation",
action="store", action="store",
+4 -15
View File
@@ -18,27 +18,16 @@ limited_api_project = test_projects.new_c_project(
IS_CPYTHON = sys.implementation.name == "cpython" IS_CPYTHON = sys.implementation.name == "cpython"
Py_GIL_DISABLED = sysconfig.get_config_var("Py_GIL_DISABLED") Py_GIL_DISABLED = sysconfig.get_config_var("Py_GIL_DISABLED")
CAN_USE_ABI3 = IS_CPYTHON and not Py_GIL_DISABLED CAN_USE_ABI3 = IS_CPYTHON and not Py_GIL_DISABLED
cmdclass = {} setup_options = {}
extension_kwargs = {} extension_kwargs = {}
if CAN_USE_ABI3 and sys.version_info[:2] >= (3, 10): if CAN_USE_ABI3 and sys.version_info[:2] >= (3, 10):
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
class bdist_wheel_abi3(_bdist_wheel):
def finalize_options(self):
_bdist_wheel.finalize_options(self)
self.root_is_pure = False
def get_tag(self):
python, abi, plat = _bdist_wheel.get_tag(self)
return python, "abi3", plat
cmdclass["bdist_wheel"] = bdist_wheel_abi3
extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x030A0000")] extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x030A0000")]
extension_kwargs["py_limited_api"] = True extension_kwargs["py_limited_api"] = True
setup_options = {"bdist_wheel": {"py_limited_api": "cp310"}}
""" """
), ),
setup_py_extension_args_add="**extension_kwargs", setup_py_extension_args_add="**extension_kwargs",
setup_py_setup_args_add="cmdclass=cmdclass", setup_py_setup_args_add="options=setup_options",
) )
limited_api_project.files["pyproject.toml"] = pyproject_toml limited_api_project.files["pyproject.toml"] = pyproject_toml
@@ -65,7 +54,7 @@ def test_abi3(tmp_path):
if utils.platform == "pyodide": if utils.platform == "pyodide":
# there's only 1 possible configuration for pyodide, the single_python_tag one # there's only 1 possible configuration for pyodide, the single_python_tag one
expected_wheels = [ expected_wheels = [
w.replace(f"{single_python_tag}-{single_python_tag}", f"{single_python_tag}-abi3") w.replace(f"{single_python_tag}-{single_python_tag}", "cp310-abi3")
for w in expected_wheels for w in expected_wheels
] ]
else: else:
+2 -4
View File
@@ -21,7 +21,7 @@ def test_podman(tmp_path, capfd, request):
actual_wheels = utils.cibuildwheel_run( actual_wheels = utils.cibuildwheel_run(
project_dir, project_dir,
add_env={ add_env={
"CIBW_ARCHS": "x86_64", "CIBW_ARCHS": "native",
"CIBW_BEFORE_ALL": "echo 'test log statement from before-all'", "CIBW_BEFORE_ALL": "echo 'test log statement from before-all'",
"CIBW_CONTAINER_ENGINE": "podman", "CIBW_CONTAINER_ENGINE": "podman",
}, },
@@ -29,9 +29,7 @@ def test_podman(tmp_path, capfd, request):
) )
# check that the expected wheels are produced # check that the expected wheels are produced
expected_wheels = [ expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True, single_arch=True)
w for w in utils.expected_wheels("spam", "0.1.0", single_python=True) if "x86_64" in w
]
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
# check that stdout is bring passed-though from container correctly # check that stdout is bring passed-though from container correctly
+4 -4
View File
@@ -33,10 +33,9 @@ def test(tmp_path, capfd):
basic_project.generate(project_dir) basic_project.generate(project_dir)
num_builds = len(utils.cibuildwheel_get_build_identifiers(project_dir)) num_builds = len(utils.cibuildwheel_get_build_identifiers(project_dir))
if num_builds > 1: expectation = (
expectation = pytest.raises(subprocess.CalledProcessError) pytest.raises(subprocess.CalledProcessError) if num_builds > 1 else does_not_raise()
else: )
expectation = does_not_raise()
with expectation as exc_info: with expectation as exc_info:
result = utils.cibuildwheel_run( result = utils.cibuildwheel_run(
@@ -48,6 +47,7 @@ def test(tmp_path, capfd):
captured = capfd.readouterr() captured = capfd.readouterr()
if num_builds > 1: if num_builds > 1:
assert exc_info is not None
assert "Build failed because a wheel named" in captured.err assert "Build failed because a wheel named" in captured.err
assert exc_info.value.returncode == 6 assert exc_info.value.returncode == 6
else: else:
+3 -2
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import platform import platform
import re import re
import textwrap import textwrap
from pathlib import Path
import pytest import pytest
@@ -46,8 +47,8 @@ build-backend = "setuptools.build_meta"
VERSION_REGEX = r"([\w-]+)==([^\s]+)" VERSION_REGEX = r"([\w-]+)==([^\s]+)"
def get_versions_from_constraint_file(constraint_file): def get_versions_from_constraint_file(constraint_file: Path) -> dict[str, str]:
constraint_file_text = constraint_file.read_text(encoding="utf8") constraint_file_text = constraint_file.read_text(encoding="utf-8")
return dict(re.findall(VERSION_REGEX, constraint_file_text)) return dict(re.findall(VERSION_REGEX, constraint_file_text))
+1 -1
View File
@@ -20,7 +20,7 @@ from pyodide.code import run_js
def check_node(): def check_node():
# cibuildwheel adds a pinned node version to the PATH # cibuildwheel adds a pinned node version to the PATH
# check it's in the PATH then, check it's the one that runs pyoodide # check it's in the PATH then, check it's the one that runs Pyodide
cibw_cache_path = Path(sys.argv[1]).resolve(strict=True) cibw_cache_path = Path(sys.argv[1]).resolve(strict=True)
# find the node executable in PATH # find the node executable in PATH
node = shutil.which("node") node = shutil.which("node")
+6 -1
View File
@@ -4,6 +4,7 @@ import os
import subprocess import subprocess
import sys import sys
import textwrap import textwrap
from collections.abc import Mapping
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
@@ -28,7 +29,11 @@ def make_sdist(project: TestProject, working_dir: Path) -> Path:
return next(sdist_dir.glob("*.tar.gz")) return next(sdist_dir.glob("*.tar.gz"))
def cibuildwheel_from_sdist_run(sdist_path, add_env=None, config_file=None): def cibuildwheel_from_sdist_run(
sdist_path: Path | str,
add_env: Mapping[str, str] | None = None,
config_file: str | None = None,
) -> list[str]:
env = os.environ.copy() env = os.environ.copy()
if add_env: if add_env:
+1 -1
View File
@@ -8,7 +8,7 @@ from argparse import ArgumentParser
from pathlib import Path from pathlib import Path
def main(): def main() -> None:
parser = ArgumentParser( parser = ArgumentParser(
prog="python -m test.test_projects", description="Generate a test project to check it out" prog="python -m test.test_projects", description="Generate a test project to check it out"
) )
+3 -3
View File
@@ -22,11 +22,11 @@ class TestProject:
files: FilesDict files: FilesDict
template_context: TemplateContext template_context: TemplateContext
def __init__(self): def __init__(self) -> None:
self.files = {} self.files = {}
self.template_context = {} self.template_context = {}
def generate(self, path: Path): def generate(self, path: Path) -> None:
for filename, content in self.files.items(): for filename, content in self.files.items():
file_path = path / filename file_path = path / filename
file_path.parent.mkdir(parents=True, exist_ok=True) file_path.parent.mkdir(parents=True, exist_ok=True)
@@ -37,7 +37,7 @@ class TestProject:
f.write(content) f.write(content)
def copy(self): def copy(self) -> TestProject:
other = TestProject() other = TestProject()
other.files = self.files.copy() other.files = self.files.copy()
other.template_context = self.template_context.copy() other.template_context = self.template_context.copy()
+7 -7
View File
@@ -78,13 +78,13 @@ version = 0.1.0
def new_c_project( def new_c_project(
*, *,
spam_c_top_level_add="", spam_c_top_level_add: str = "",
spam_c_function_add="", spam_c_function_add: str = "",
setup_py_add="", setup_py_add: str = "",
setup_py_extension_args_add="", setup_py_extension_args_add: str = "",
setup_py_setup_args_add="", setup_py_setup_args_add: str = "",
setup_cfg_add="", setup_cfg_add: str = "",
): ) -> TestProject:
project = TestProject() project = TestProject()
project.files.update( project.files.update(
+20 -8
View File
@@ -1,25 +1,37 @@
from __future__ import annotations
import textwrap import textwrap
import pytest
from . import test_projects, utils from . import test_projects, utils
project_with_ssl_tests = test_projects.new_c_project( project_with_ssl_tests = test_projects.new_c_project(
setup_py_add=textwrap.dedent( setup_py_add=textwrap.dedent(
r""" r"""
import ssl import ssl, time, urllib.request, urllib.error
from urllib.request import urlopen def check_https(context=None):
# google hosts this endpoint that returns a 204 No Content, it's used for
# connectivity checks in Android & Chrome
url = "https://google.com/generate_204"
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) for i in range(5):
data = urlopen("https://www.nist.gov", context=context) try:
data = urlopen("https://raw.githubusercontent.com/pypa/cibuildwheel/main/CI.md", context=context) urllib.request.urlopen(url, context=context, timeout=5)
data = urlopen("https://raw.githubusercontent.com/pypa/cibuildwheel/main/CI.md") return
except (OSError, urllib.error.URLError) as e:
print(f"Attempt {i+1}: Could not connect to {url}: {e}")
time.sleep(2 ** i) # Backoff: 1s, 2s, 4s, 8s...
raise ConnectionError(f"Could not connect to {url} after retries.")
check_https()
check_https(context=ssl.SSLContext(ssl.PROTOCOL_TLSv1_2))
""" """
) )
) )
@pytest.mark.flaky(reruns=2)
def test(tmp_path): def test(tmp_path):
# this test checks that SSL is working in the build environment using # this test checks that SSL is working in the build environment using
# some checks in setup.py. # some checks in setup.py.
+34 -1
View File
@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import inspect
import os import os
import subprocess import subprocess
import textwrap import textwrap
@@ -114,6 +115,38 @@ def test_extras_require(tmp_path):
assert set(actual_wheels) == set(expected_wheels) assert set(actual_wheels) == set(expected_wheels)
def test_dependency_groups(tmp_path):
group_project = project_with_a_test.copy()
group_project.files["pyproject.toml"] = inspect.cleandoc("""
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[dependency-groups]
dev = ["pytest"]
""")
project_dir = tmp_path / "project"
group_project.generate(project_dir)
# build and test the wheels
actual_wheels = utils.cibuildwheel_run(
project_dir,
add_env={
"CIBW_TEST_GROUPS": "dev",
# the 'false ||' bit is to ensure this command runs in a shell on
# mac/linux.
"CIBW_TEST_COMMAND": f"false || {utils.invoke_pytest()} {{project}}/test",
"CIBW_TEST_COMMAND_WINDOWS": "COLOR 00 || pytest {project}/test",
},
single_python=True,
)
# also check that we got the right wheels
expected_wheels = utils.expected_wheels("spam", "0.1.0", single_python=True)
assert set(actual_wheels) == set(expected_wheels)
project_with_a_failing_test = test_projects.new_c_project() project_with_a_failing_test = test_projects.new_c_project()
project_with_a_failing_test.files["test/spam_test.py"] = r""" project_with_a_failing_test.files["test/spam_test.py"] = r"""
from unittest import TestCase from unittest import TestCase
@@ -155,7 +188,7 @@ def test_failing_test(tmp_path):
@pytest.mark.parametrize("test_runner", ["pytest", "unittest"]) @pytest.mark.parametrize("test_runner", ["pytest", "unittest"])
def test_bare_pytest_invocation( def test_bare_pytest_invocation(
tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str
): ) -> None:
"""Check that if a user runs pytest in the the test cwd, it raises a helpful error""" """Check that if a user runs pytest in the the test cwd, it raises a helpful error"""
project_dir = tmp_path / "project" project_dir = tmp_path / "project"
output_dir = tmp_path / "output" output_dir = tmp_path / "output"
+1 -1
View File
@@ -11,7 +11,7 @@ from . import test_projects, utils
basic_project = test_projects.new_c_project() basic_project = test_projects.new_c_project()
def skip_if_no_msvc(arm64=False): def skip_if_no_msvc(arm64: bool = False) -> None:
programfiles = os.getenv("PROGRAMFILES(X86)", "") or os.getenv("PROGRAMFILES", "") programfiles = os.getenv("PROGRAMFILES(X86)", "") or os.getenv("PROGRAMFILES", "")
if not programfiles: if not programfiles:
pytest.skip("Requires %PROGRAMFILES(X86)% variable to be set") pytest.skip("Requires %PROGRAMFILES(X86)% variable to be set")
+50 -40
View File
@@ -10,8 +10,10 @@ import os
import platform as pm import platform as pm
import subprocess import subprocess
import sys import sys
from collections.abc import Mapping, Sequence
from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Final from typing import Any, Final
import pytest import pytest
@@ -37,7 +39,9 @@ else:
raise Exception(msg) raise Exception(msg)
def cibuildwheel_get_build_identifiers(project_path, env=None, *, prerelease_pythons=False): def cibuildwheel_get_build_identifiers(
project_path: Path, env: dict[str, str] | None = None, *, prerelease_pythons: bool = False
) -> list[str]:
""" """
Returns the list of build identifiers that cibuildwheel will try to build Returns the list of build identifiers that cibuildwheel will try to build
for the current platform. for the current platform.
@@ -75,14 +79,14 @@ def _update_pip_cache_dir(env: dict[str, str]) -> None:
def cibuildwheel_run( def cibuildwheel_run(
project_path, project_path: str | Path,
package_dir=".", package_dir: str | Path = ".",
env=None, env: dict[str, str] | None = None,
add_env=None, add_env: Mapping[str, str] | None = None,
output_dir=None, output_dir: Path | None = None,
add_args=None, add_args: Sequence[str] | None = None,
single_python=False, single_python: bool = False,
): ) -> list[str]:
""" """
Runs cibuildwheel as a subprocess, building the project at project_path. Runs cibuildwheel as a subprocess, building the project at project_path.
@@ -144,17 +148,17 @@ def _floor_macosx(*args: str) -> str:
def expected_wheels( def expected_wheels(
package_name, package_name: str,
package_version, package_version: str,
manylinux_versions=None, manylinux_versions: list[str] | None = None,
musllinux_versions=None, musllinux_versions: list[str] | None = None,
macosx_deployment_target="10.9", macosx_deployment_target: str = "10.9",
machine_arch=None, machine_arch: str | None = None,
python_abi_tags=None, python_abi_tags: list[str] | None = None,
include_universal2=False, include_universal2: bool = False,
single_python=False, single_python: bool = False,
single_arch=False, single_arch: bool = False,
): ) -> list[str]:
""" """
Returns a list of expected wheels from a run of cibuildwheel. Returns a list of expected wheels from a run of cibuildwheel.
""" """
@@ -170,7 +174,9 @@ def expected_wheels(
machine_arch = "aarch64" machine_arch = "aarch64"
if manylinux_versions is None: if manylinux_versions is None:
if machine_arch == "x86_64": if machine_arch == "armv7l":
manylinux_versions = ["manylinux_2_17", "manylinux2014", "manylinux_2_31"]
elif machine_arch == "x86_64":
manylinux_versions = [ manylinux_versions = [
"manylinux_2_5", "manylinux_2_5",
"manylinux1", "manylinux1",
@@ -204,6 +210,7 @@ def expected_wheels(
"pp38-pypy38_pp73", "pp38-pypy38_pp73",
"pp39-pypy39_pp73", "pp39-pypy39_pp73",
"pp310-pypy310_pp73", "pp310-pypy310_pp73",
"pp311-pypy311_pp73",
] ]
if platform == "macos" and machine_arch == "arm64": if platform == "macos" and machine_arch == "arm64":
@@ -219,6 +226,7 @@ def expected_wheels(
"pp38-pypy38_pp73", "pp38-pypy38_pp73",
"pp39-pypy39_pp73", "pp39-pypy39_pp73",
"pp310-pypy310_pp73", "pp310-pypy310_pp73",
"pp311-pypy311_pp73",
] ]
if single_python: if single_python:
@@ -274,25 +282,27 @@ def expected_wheels(
platform_tags = ["win32", "win_amd64"] platform_tags = ["win32", "win_amd64"]
elif platform == "macos": elif platform == "macos":
if python_abi_tag.startswith("pp"):
if python_abi_tag.startswith(("pp37", "pp38")):
min_macosx = macosx_deployment_target
else:
min_macosx = _floor_macosx(macosx_deployment_target, "10.15")
elif python_abi_tag.startswith("cp"):
if python_abi_tag.startswith(("cp36", "cp37", "cp38", "cp39", "cp310", "cp311")):
min_macosx = macosx_deployment_target
else:
min_macosx = _floor_macosx(macosx_deployment_target, "10.13")
else:
min_macosx = macosx_deployment_target
if machine_arch == "arm64": if machine_arch == "arm64":
arm64_macosx = _floor_macosx(macosx_deployment_target, "11.0") arm64_macosx = _floor_macosx(min_macosx, "11.0")
platform_tags = [f'macosx_{arm64_macosx.replace(".", "_")}_arm64'] platform_tags = [f'macosx_{arm64_macosx.replace(".", "_")}_arm64']
else: else:
if python_abi_tag.startswith("pp") and not python_abi_tag.startswith( platform_tags = [f'macosx_{min_macosx.replace(".", "_")}_x86_64']
("pp37", "pp38")
):
pypy_macosx = _floor_macosx(macosx_deployment_target, "10.15")
platform_tags = [f'macosx_{pypy_macosx.replace(".", "_")}_x86_64']
elif python_abi_tag.startswith("cp313"):
pypy_macosx = _floor_macosx(macosx_deployment_target, "10.13")
platform_tags = [f'macosx_{pypy_macosx.replace(".", "_")}_x86_64']
else:
platform_tags = [f'macosx_{macosx_deployment_target.replace(".", "_")}_x86_64']
if include_universal2: if include_universal2:
platform_tags.append( platform_tags.append(f'macosx_{min_macosx.replace(".", "_")}_universal2')
f'macosx_{macosx_deployment_target.replace(".", "_")}_universal2',
)
else: else:
msg = f"Unsupported platform {platform!r}" msg = f"Unsupported platform {platform!r}"
raise Exception(msg) raise Exception(msg)
@@ -303,7 +313,7 @@ def expected_wheels(
return wheels return wheels
def get_macos_version(): def get_macos_version() -> tuple[int, int]:
""" """
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0) Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
@@ -312,10 +322,10 @@ def get_macos_version():
(11, 2) <= (11, 0) != True (11, 2) <= (11, 0) != True
""" """
version_str, _, _ = pm.mac_ver() version_str, _, _ = pm.mac_ver()
return tuple(map(int, version_str.split(".")[:2])) return tuple(map(int, version_str.split(".")[:2])) # type: ignore[return-value]
def skip_if_pyodide(reason: str): def skip_if_pyodide(reason: str) -> Any:
return pytest.mark.skipif(platform == "pyodide", reason=reason) return pytest.mark.skipif(platform == "pyodide", reason=reason)
@@ -326,7 +336,7 @@ def invoke_pytest() -> str:
return "pytest" return "pytest"
def arch_name_for_linux(arch: str): def arch_name_for_linux(arch: str) -> str:
""" """
Archs have different names on different platforms, but it's useful to be Archs have different names on different platforms, but it's useful to be
able to run linux tests on dev machines. This function translates between able to run linux tests on dev machines. This function translates between
+16 -9
View File
@@ -2,11 +2,13 @@ from __future__ import annotations
from packaging.specifiers import SpecifierSet from packaging.specifiers import SpecifierSet
from cibuildwheel.util import BuildSelector from cibuildwheel.util import BuildSelector, EnableGroups
def test_build(): def test_build():
build_selector = BuildSelector(build_config="cp3*-* *-manylinux*", skip_config="") build_selector = BuildSelector(
build_config="cp3*-* *-manylinux*", skip_config="", enable=frozenset([EnableGroups.PyPy])
)
assert build_selector("cp36-manylinux_x86_64") assert build_selector("cp36-manylinux_x86_64")
assert build_selector("cp37-manylinux_x86_64") assert build_selector("cp37-manylinux_x86_64")
@@ -43,7 +45,7 @@ def test_build_filter_pre():
build_selector = BuildSelector( build_selector = BuildSelector(
build_config="cp3*-* *-manylinux*", build_config="cp3*-* *-manylinux*",
skip_config="", skip_config="",
prerelease_pythons=True, enable=frozenset([EnableGroups.CPythonPrerelease, EnableGroups.PyPy]),
) )
assert build_selector("cp37-manylinux_x86_64") assert build_selector("cp37-manylinux_x86_64")
@@ -55,7 +57,9 @@ def test_build_filter_pre():
def test_skip(): def test_skip():
build_selector = BuildSelector( build_selector = BuildSelector(
build_config="*", skip_config="pp36-* cp3?-manylinux_i686 cp36-win* *-win32" build_config="*",
skip_config="pp36-* cp3?-manylinux_i686 cp36-win* *-win32",
enable=frozenset([EnableGroups.PyPy]),
) )
assert not build_selector("pp36-manylinux_x86_64") assert not build_selector("pp36-manylinux_x86_64")
@@ -79,7 +83,9 @@ def test_skip():
def test_build_and_skip(): def test_build_and_skip():
build_selector = BuildSelector( build_selector = BuildSelector(
build_config="cp36-* cp37-macosx* *-manylinux*", skip_config="pp37-* cp37-manylinux_i686" build_config="cp36-* cp37-macosx* *-manylinux*",
skip_config="pp37-* cp37-manylinux_i686",
enable=frozenset([EnableGroups.PyPy]),
) )
assert not build_selector("pp37-manylinux_x86_64") assert not build_selector("pp37-manylinux_x86_64")
@@ -110,7 +116,10 @@ def test_build_braces():
def test_build_limited_python(): def test_build_limited_python():
build_selector = BuildSelector( build_selector = BuildSelector(
build_config="*", skip_config="", requires_python=SpecifierSet(">=3.7") build_config="*",
skip_config="",
requires_python=SpecifierSet(">=3.7"),
enable=frozenset([EnableGroups.PyPy]),
) )
assert not build_selector("cp36-manylinux_x86_64") assert not build_selector("cp36-manylinux_x86_64")
@@ -146,9 +155,7 @@ def test_build_limited_python_patch():
def test_build_free_threaded_python(): def test_build_free_threaded_python():
build_selector = BuildSelector( build_selector = BuildSelector(build_config="*", skip_config="", enable=frozenset(EnableGroups))
build_config="*", skip_config="", prerelease_pythons=True, free_threaded_support=True
)
assert build_selector("cp313t-manylinux_x86_64") assert build_selector("cp313t-manylinux_x86_64")
+6 -4
View File
@@ -4,12 +4,14 @@ import textwrap
from pathlib import Path from pathlib import Path
from pprint import pprint from pprint import pprint
import pytest
import cibuildwheel.linux import cibuildwheel.linux
from cibuildwheel.oci_container import OCIContainerEngineConfig from cibuildwheel.oci_container import OCIContainerEngineConfig
from cibuildwheel.options import CommandLineArguments, Options from cibuildwheel.options import CommandLineArguments, Options
def test_linux_container_split(tmp_path: Path, monkeypatch): def test_linux_container_split(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
""" """
Tests splitting linux builds by container image, container engine, and before_all Tests splitting linux builds by container image, container engine, and before_all
""" """
@@ -53,13 +55,13 @@ def test_linux_container_split(tmp_path: Path, monkeypatch):
build_steps = list(cibuildwheel.linux.get_build_steps(options, python_configurations)) build_steps = list(cibuildwheel.linux.get_build_steps(options, python_configurations))
# helper functions to extract test info # helper functions to extract test info
def identifiers(step): def identifiers(step: cibuildwheel.linux.BuildStep) -> list[str]:
return [c.identifier for c in step.platform_configs] return [c.identifier for c in step.platform_configs]
def before_alls(step): def before_alls(step: cibuildwheel.linux.BuildStep) -> list[str]:
return [options.build_options(c.identifier).before_all for c in step.platform_configs] return [options.build_options(c.identifier).before_all for c in step.platform_configs]
def container_engines(step): def container_engines(step: cibuildwheel.linux.BuildStep) -> list[OCIContainerEngineConfig]:
return [options.build_options(c.identifier).container_engine for c in step.platform_configs] return [options.build_options(c.identifier).container_engine for c in step.platform_configs]
pprint(build_steps) pprint(build_steps)
+4 -4
View File
@@ -12,12 +12,12 @@ from cibuildwheel import linux, macos, util, windows
class ArgsInterceptor: class ArgsInterceptor:
def __init__(self): def __init__(self) -> None:
self.call_count = 0 self.call_count = 0
self.args = None self.args: tuple[object, ...] | None = None
self.kwargs = None self.kwargs: dict[str, object] | None = None
def __call__(self, *args, **kwargs): def __call__(self, *args: object, **kwargs: object) -> None:
self.call_count += 1 self.call_count += 1
self.args = args self.args = args
self.kwargs = kwargs self.kwargs = kwargs
+2 -2
View File
@@ -119,7 +119,7 @@ def test_manylinux_images(
assert build_options.manylinux_images is None assert build_options.manylinux_images is None
def get_default_repair_command(platform): def get_default_repair_command(platform: str) -> str:
if platform == "linux": if platform == "linux":
return "auditwheel repair -w {dest_dir} {wheel}" return "auditwheel repair -w {dest_dir} {wheel}"
elif platform == "macos": elif platform == "macos":
@@ -270,7 +270,7 @@ def test_config_settings(platform_specific, platform, intercepted_build_args, mo
config_settings = 'setting=value setting=value2 other="something else"' config_settings = 'setting=value setting=value2 other="something else"'
if platform_specific: if platform_specific:
monkeypatch.setenv("CIBW_CONFIG_SETTINGS_" + platform.upper(), config_settings) monkeypatch.setenv("CIBW_CONFIG_SETTINGS_" + platform.upper(), config_settings)
monkeypatch.setenv("CIBW_CONFIG_SETTIGNS", "a=b") monkeypatch.setenv("CIBW_CONFIG_SETTINGS", "a=b")
else: else:
monkeypatch.setenv("CIBW_CONFIG_SETTINGS", config_settings) monkeypatch.setenv("CIBW_CONFIG_SETTINGS", config_settings)
+3 -1
View File
@@ -6,6 +6,7 @@ import pytest
from cibuildwheel.__main__ import main from cibuildwheel.__main__ import main
from cibuildwheel.architecture import Architecture from cibuildwheel.architecture import Architecture
from cibuildwheel.util import EnableGroups
from ..conftest import MOCK_PACKAGE_DIR from ..conftest import MOCK_PACKAGE_DIR
@@ -179,6 +180,7 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch):
Architecture.aarch64, Architecture.aarch64,
Architecture.ppc64le, Architecture.ppc64le,
Architecture.s390x, Architecture.s390x,
Architecture.armv7l,
} }
elif platform == "windows": elif platform == "windows":
assert options.globals.architectures == { assert options.globals.architectures == {
@@ -215,7 +217,7 @@ def test_only_argument(intercepted_build_args, monkeypatch, only, plat):
assert options.globals.build_selector.skip_config == "" assert options.globals.build_selector.skip_config == ""
assert options.platform == plat assert options.platform == plat
assert options.globals.architectures == Architecture.all_archs(plat) assert options.globals.architectures == Architecture.all_archs(plat)
assert options.globals.build_selector.prerelease_pythons is True assert EnableGroups.PyPy in options.globals.build_selector.enable
@pytest.mark.parametrize("only", ("cp311-manylxinux_x86_64", "some_linux_thing")) @pytest.mark.parametrize("only", ("cp311-manylxinux_x86_64", "some_linux_thing"))
+279 -44
View File
@@ -6,14 +6,24 @@ import platform
import random import random
import shutil import shutil
import subprocess import subprocess
import sys
import textwrap import textwrap
import time
from contextlib import nullcontext
from pathlib import Path, PurePath, PurePosixPath from pathlib import Path, PurePath, PurePosixPath
import pytest import pytest
import tomli_w import tomli_w
import cibuildwheel.oci_container
from cibuildwheel.environment import EnvironmentAssignmentBash from cibuildwheel.environment import EnvironmentAssignmentBash
from cibuildwheel.oci_container import OCIContainer, OCIContainerEngineConfig from cibuildwheel.errors import OCIEngineTooOldError
from cibuildwheel.oci_container import (
OCIContainer,
OCIContainerEngineConfig,
OCIPlatform,
_check_engine_version,
)
from cibuildwheel.util import CIProvider, detect_ci_provider from cibuildwheel.util import CIProvider, detect_ci_provider
# Test utilities # Test utilities
@@ -28,6 +38,14 @@ elif pm in {"aarch64", "arm64"}:
DEFAULT_IMAGE = DEFAULT_IMAGE_TEMPLATE.format(machine="aarch64") DEFAULT_IMAGE = DEFAULT_IMAGE_TEMPLATE.format(machine="aarch64")
else: else:
DEFAULT_IMAGE = "" DEFAULT_IMAGE = ""
DEFAULT_OCI_PLATFORM = {
"AMD64": OCIPlatform.AMD64,
"x86_64": OCIPlatform.AMD64,
"ppc64le": OCIPlatform.PPC64LE,
"s390x": OCIPlatform.S390X,
"aarch64": OCIPlatform.ARM64,
"arm64": OCIPlatform.ARM64,
}[pm]
PODMAN = OCIContainerEngineConfig(name="podman") PODMAN = OCIContainerEngineConfig(name="podman")
@@ -63,24 +81,32 @@ def container_engine(request):
def test_simple(container_engine): def test_simple(container_engine):
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
assert container.call(["echo", "hello"], capture_output=True) == "hello\n" assert container.call(["echo", "hello"], capture_output=True) == "hello\n"
def test_no_lf(container_engine): def test_no_lf(container_engine):
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
assert container.call(["printf", "hello"], capture_output=True) == "hello" assert container.call(["printf", "hello"], capture_output=True) == "hello"
def test_debug_info(container_engine): def test_debug_info(container_engine):
container = OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) container = OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
)
print(container.debug_info()) print(container.debug_info())
with container: with container:
pass pass
def test_environment(container_engine): def test_environment(container_engine):
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
assert ( assert (
container.call( container.call(
["sh", "-c", "echo $TEST_VAR"], env={"TEST_VAR": "1"}, capture_output=True ["sh", "-c", "echo $TEST_VAR"], env={"TEST_VAR": "1"}, capture_output=True
@@ -92,7 +118,9 @@ def test_environment(container_engine):
def test_environment_pass(container_engine, monkeypatch): def test_environment_pass(container_engine, monkeypatch):
monkeypatch.setenv("CIBUILDWHEEL", "1") monkeypatch.setenv("CIBUILDWHEEL", "1")
monkeypatch.setenv("SOURCE_DATE_EPOCH", "1489957071") monkeypatch.setenv("SOURCE_DATE_EPOCH", "1489957071")
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
assert container.call(["sh", "-c", "echo $CIBUILDWHEEL"], capture_output=True) == "1\n" assert container.call(["sh", "-c", "echo $CIBUILDWHEEL"], capture_output=True) == "1\n"
assert ( assert (
container.call(["sh", "-c", "echo $SOURCE_DATE_EPOCH"], capture_output=True) container.call(["sh", "-c", "echo $SOURCE_DATE_EPOCH"], capture_output=True)
@@ -102,14 +130,37 @@ def test_environment_pass(container_engine, monkeypatch):
def test_cwd(container_engine): def test_cwd(container_engine):
with OCIContainer( with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, cwd="/cibuildwheel/working_directory" engine=container_engine,
image=DEFAULT_IMAGE,
oci_platform=DEFAULT_OCI_PLATFORM,
cwd="/cibuildwheel/working_directory",
) as container: ) as container:
assert container.call(["pwd"], capture_output=True) == "/cibuildwheel/working_directory\n" assert container.call(["pwd"], capture_output=True) == "/cibuildwheel/working_directory\n"
assert container.call(["pwd"], capture_output=True, cwd="/opt") == "/opt\n" assert container.call(["pwd"], capture_output=True, cwd="/opt") == "/opt\n"
def test_container_removed(container_engine): def test_container_removed(container_engine):
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: # test is flaky on some platforms, implement retry for 5 second
timeout = 50 # * 100 ms = 5s
with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
assert container.name is not None
container_name = container.name
for _ in range(timeout):
docker_containers_listing = subprocess.run(
f"{container.engine.name} container ls",
shell=True,
check=True,
stdout=subprocess.PIPE,
text=True,
).stdout
if container_name in docker_containers_listing:
break
time.sleep(0.1)
assert container_name in docker_containers_listing
for _ in range(timeout):
docker_containers_listing = subprocess.run( docker_containers_listing = subprocess.run(
f"{container.engine.name} container ls", f"{container.engine.name} container ls",
shell=True, shell=True,
@@ -117,18 +168,10 @@ def test_container_removed(container_engine):
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
text=True, text=True,
).stdout ).stdout
assert container.name is not None if container_name not in docker_containers_listing:
assert container.name in docker_containers_listing break
old_container_name = container.name time.sleep(0.1)
assert container_name not in docker_containers_listing
docker_containers_listing = subprocess.run(
f"{container.engine.name} container ls",
shell=True,
check=True,
stdout=subprocess.PIPE,
text=True,
).stdout
assert old_container_name not in docker_containers_listing
def test_large_environment(container_engine): def test_large_environment(container_engine):
@@ -141,7 +184,9 @@ def test_large_environment(container_engine):
"d": "0" * long_env_var_length, "d": "0" * long_env_var_length,
} }
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
# check the length of d # check the length of d
assert ( assert (
container.call(["sh", "-c", "echo ${#d}"], env=large_environment, capture_output=True) container.call(["sh", "-c", "echo ${#d}"], env=large_environment, capture_output=True)
@@ -150,7 +195,9 @@ def test_large_environment(container_engine):
def test_binary_output(container_engine): def test_binary_output(container_engine):
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
# note: the below embedded snippets are in python2 # note: the below embedded snippets are in python2
# check that we can pass though arbitrary binary data without erroring # check that we can pass though arbitrary binary data without erroring
@@ -199,8 +246,10 @@ def test_binary_output(container_engine):
assert output == binary_data_string assert output == binary_data_string
def test_file_operation(tmp_path: Path, container_engine): def test_file_operation(tmp_path: Path, container_engine: OCIContainerEngineConfig) -> None:
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
# test copying a file in # test copying a file in
test_binary_data = bytes(random.randrange(256) for _ in range(1000)) test_binary_data = bytes(random.randrange(256) for _ in range(1000))
original_test_file = tmp_path / "test.dat" original_test_file = tmp_path / "test.dat"
@@ -210,12 +259,17 @@ def test_file_operation(tmp_path: Path, container_engine):
container.copy_into(original_test_file, dst_file) container.copy_into(original_test_file, dst_file)
owner = container.call(["stat", "-c", "%u:%g", dst_file], capture_output=True).strip()
assert owner == "0:0"
output = container.call(["cat", dst_file], capture_output=True) output = container.call(["cat", dst_file], capture_output=True)
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape") assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
def test_dir_operations(tmp_path: Path, container_engine): def test_dir_operations(tmp_path: Path, container_engine: OCIContainerEngineConfig) -> None:
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
test_binary_data = bytes(random.randrange(256) for _ in range(1000)) test_binary_data = bytes(random.randrange(256) for _ in range(1000))
original_test_file = tmp_path / "test.dat" original_test_file = tmp_path / "test.dat"
original_test_file.write_bytes(test_binary_data) original_test_file.write_bytes(test_binary_data)
@@ -230,6 +284,12 @@ def test_dir_operations(tmp_path: Path, container_engine):
dst_file = dst_dir / "test.dat" dst_file = dst_dir / "test.dat"
container.copy_into(test_dir, dst_dir) container.copy_into(test_dir, dst_dir)
owner = container.call(["stat", "-c", "%u:%g", dst_dir], capture_output=True).strip()
assert owner == "0:0"
owner = container.call(["stat", "-c", "%u:%g", dst_file], capture_output=True).strip()
assert owner == "0:0"
output = container.call(["cat", dst_file], capture_output=True) output = container.call(["cat", dst_file], capture_output=True)
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape") assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
@@ -240,18 +300,29 @@ def test_dir_operations(tmp_path: Path, container_engine):
new_test_dir = tmp_path / "test_dir_new" new_test_dir = tmp_path / "test_dir_new"
container.copy_out(dst_dir, new_test_dir) container.copy_out(dst_dir, new_test_dir)
assert os.getuid() == new_test_dir.stat().st_uid
assert os.getgid() == new_test_dir.stat().st_gid
assert os.getuid() == (new_test_dir / "test.dat").stat().st_uid
assert os.getgid() == (new_test_dir / "test.dat").stat().st_gid
assert test_binary_data == (new_test_dir / "test.dat").read_bytes() assert test_binary_data == (new_test_dir / "test.dat").read_bytes()
def test_environment_executor(container_engine): def test_environment_executor(container_engine: OCIContainerEngineConfig) -> None:
with OCIContainer(engine=container_engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
assignment = EnvironmentAssignmentBash("TEST=$(echo 42)") assignment = EnvironmentAssignmentBash("TEST=$(echo 42)")
assert assignment.evaluated_value({}, container.environment_executor) == "42" assert assignment.evaluated_value({}, container.environment_executor) == "42"
def test_podman_vfs(tmp_path: Path, monkeypatch, container_engine): def test_podman_vfs(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, container_engine: OCIContainerEngineConfig
) -> None:
if container_engine.name != "podman": if container_engine.name != "podman":
pytest.skip("only runs with podman") pytest.skip("only runs with podman")
if sys.platform.startswith("darwin"):
pytest.skip("Skipping test because podman on this platform does not support vfs")
# create the VFS configuration # create the VFS configuration
vfs_path = tmp_path / "podman_vfs" vfs_path = tmp_path / "podman_vfs"
@@ -309,7 +380,9 @@ def test_podman_vfs(tmp_path: Path, monkeypatch, container_engine):
monkeypatch.setenv("CONTAINERS_CONF", str(vfs_containers_conf_fpath)) monkeypatch.setenv("CONTAINERS_CONF", str(vfs_containers_conf_fpath))
monkeypatch.setenv("CONTAINERS_STORAGE_CONF", str(vfs_containers_storage_conf_fpath)) monkeypatch.setenv("CONTAINERS_STORAGE_CONF", str(vfs_containers_storage_conf_fpath))
with OCIContainer(engine=PODMAN, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=PODMAN, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
# test running a command # test running a command
assert container.call(["echo", "hello"], capture_output=True) == "hello\n" assert container.call(["echo", "hello"], capture_output=True) == "hello\n"
@@ -327,7 +400,7 @@ def test_podman_vfs(tmp_path: Path, monkeypatch, container_engine):
subprocess.run(["podman", "unshare", "rm", "-rf", vfs_path], check=True) subprocess.run(["podman", "unshare", "rm", "-rf", vfs_path], check=True)
def test_create_args_volume(tmp_path: Path, container_engine): def test_create_args_volume(tmp_path: Path, container_engine: OCIContainerEngineConfig) -> None:
if container_engine.name != "docker": if container_engine.name != "docker":
pytest.skip("only runs with docker") pytest.skip("only runs with docker")
@@ -346,6 +419,7 @@ def test_create_args_volume(tmp_path: Path, container_engine):
with OCIContainer( with OCIContainer(
engine=container_engine, engine=container_engine,
image=DEFAULT_IMAGE, image=DEFAULT_IMAGE,
oci_platform=DEFAULT_OCI_PLATFORM,
) as container: ) as container:
assert container.call(["cat", "/test_mount/test_file.txt"], capture_output=True) == "1234" assert container.call(["cat", "/test_mount/test_file.txt"], capture_output=True) == "1234"
@@ -388,24 +462,47 @@ def test_create_args_volume(tmp_path: Path, container_engine):
"docker", "docker",
("--some-option=value; with; semicolons", "--another-option"), ("--some-option=value; with; semicolons", "--another-option"),
), ),
(
"docker; create_args: --platform=linux/amd64",
"docker",
(),
),
(
"podman; create_args: --platform=linux/amd64",
"podman",
(),
),
(
"docker; create_args: --platform linux/amd64",
"docker",
(),
),
(
"podman; create_args: --platform linux/amd64",
"podman",
(),
),
], ],
) )
def test_parse_engine_config(config, name, create_args): def test_parse_engine_config(config, name, create_args, capsys):
engine_config = OCIContainerEngineConfig.from_config_string(config) engine_config = OCIContainerEngineConfig.from_config_string(config)
assert engine_config.name == name assert engine_config.name == name
assert engine_config.create_args == create_args assert engine_config.create_args == create_args
if "--platform" in config:
captured = capsys.readouterr()
assert (
"Using '--platform' in 'container-engine::create_args' is deprecated. It will be ignored."
in captured.err
)
@pytest.mark.skipif(pm != "x86_64", reason="Only runs on x86_64") @pytest.mark.skipif(pm != "x86_64", reason="Only runs on x86_64")
@pytest.mark.parametrize( def test_enforce_32_bit(container_engine):
("image", "shell_args"), with OCIContainer(
[ engine=container_engine,
(DEFAULT_IMAGE_TEMPLATE.format(machine="i686"), ["/bin/bash"]), image=DEFAULT_IMAGE_TEMPLATE.format(machine="i686"),
(DEFAULT_IMAGE_TEMPLATE.format(machine="x86_64"), ["linux32", "/bin/bash"]), oci_platform=OCIPlatform.i386,
], ) as container:
)
def test_enforce_32_bit(container_engine, image, shell_args):
with OCIContainer(engine=container_engine, image=image, enforce_32_bit=True) as container:
assert container.call(["uname", "-m"], capture_output=True).strip() == "i686" assert container.call(["uname", "-m"], capture_output=True).strip() == "i686"
container_args = subprocess.run( container_args = subprocess.run(
f"{container.engine.name} inspect -f '{{{{json .Args }}}}' {container.name}", f"{container.engine.name} inspect -f '{{{{json .Args }}}}' {container.name}",
@@ -414,7 +511,7 @@ def test_enforce_32_bit(container_engine, image, shell_args):
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
text=True, text=True,
).stdout ).stdout
assert json.loads(container_args) == shell_args assert json.loads(container_args) == ["/bin/bash"]
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -425,19 +522,157 @@ def test_enforce_32_bit(container_engine, image, shell_args):
("{name}; disable_host_mount: true", False), ("{name}; disable_host_mount: true", False),
], ],
) )
def test_disable_host_mount(tmp_path: Path, container_engine, config, should_have_host_mount): def test_disable_host_mount(
tmp_path: Path,
container_engine: OCIContainerEngineConfig,
config: str,
should_have_host_mount: bool,
) -> None:
if detect_ci_provider() in {CIProvider.circle_ci, CIProvider.gitlab}: if detect_ci_provider() in {CIProvider.circle_ci, CIProvider.gitlab}:
pytest.skip("Skipping test because docker on this platform does not support host mounts") pytest.skip("Skipping test because docker on this platform does not support host mounts")
if sys.platform.startswith("darwin"):
pytest.skip("Skipping test because docker on this platform does not support host mounts")
engine = OCIContainerEngineConfig.from_config_string(config.format(name=container_engine.name)) engine = OCIContainerEngineConfig.from_config_string(config.format(name=container_engine.name))
sentinel_file = tmp_path / "sentinel" sentinel_file = tmp_path / "sentinel"
sentinel_file.write_text("12345") sentinel_file.write_text("12345")
with OCIContainer(engine=engine, image=DEFAULT_IMAGE) as container: with OCIContainer(
engine=engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM
) as container:
host_mount_path = "/host" + str(sentinel_file) host_mount_path = "/host" + str(sentinel_file)
if should_have_host_mount: if should_have_host_mount:
assert container.call(["cat", host_mount_path], capture_output=True) == "12345" assert container.call(["cat", host_mount_path], capture_output=True) == "12345"
else: else:
with pytest.raises(subprocess.CalledProcessError): with pytest.raises(subprocess.CalledProcessError):
container.call(["cat", host_mount_path], capture_output=True) container.call(["cat", host_mount_path], capture_output=True)
@pytest.mark.parametrize("platform", list(OCIPlatform))
def test_local_image(
container_engine: OCIContainerEngineConfig, platform: OCIPlatform, tmp_path: Path
) -> None:
if (
detect_ci_provider() in {CIProvider.travis_ci}
and pm in {"s390x", "ppc64le"}
and platform != DEFAULT_OCI_PLATFORM
):
pytest.skip("Skipping test because docker on this platform does not support QEMU")
if container_engine.name == "podman" and platform == OCIPlatform.ARMV7:
# both GHA & local macOS arm64 podman desktop are failing
pytest.xfail("podman fails with armv7l images")
remote_image = "debian:12-slim"
platform_name = platform.value.replace("/", "_")
local_image = f"cibw_{container_engine.name}_{platform_name}_local:latest"
dockerfile = tmp_path / "Dockerfile"
dockerfile.write_text(f"FROM {remote_image}")
subprocess.run(
[container_engine.name, "pull", f"--platform={platform.value}", remote_image],
check=True,
)
subprocess.run(
[container_engine.name, "build", f"--platform={platform.value}", "-t", local_image, "."],
check=True,
cwd=tmp_path,
)
with OCIContainer(engine=container_engine, image=local_image, oci_platform=platform):
pass
@pytest.mark.parametrize("platform", list(OCIPlatform))
def test_multiarch_image(container_engine, platform):
if (
detect_ci_provider() in {CIProvider.travis_ci}
and pm in {"s390x", "ppc64le"}
and platform != DEFAULT_OCI_PLATFORM
):
pytest.skip("Skipping test because docker on this platform does not support QEMU")
if container_engine.name == "podman" and platform == OCIPlatform.ARMV7:
# both GHA & local macOS arm64 podman desktop are failing
pytest.xfail("podman fails with armv7l images")
with OCIContainer(
engine=container_engine, image="debian:12-slim", oci_platform=platform
) as container:
output = container.call(["uname", "-m"], capture_output=True)
output_map_kernel = {
OCIPlatform.i386: ("i686",),
OCIPlatform.AMD64: ("x86_64",),
OCIPlatform.ARMV7: ("armv7l", "armv8l"),
OCIPlatform.ARM64: ("aarch64",),
OCIPlatform.PPC64LE: ("ppc64le",),
OCIPlatform.S390X: ("s390x",),
}
assert output.strip() in output_map_kernel[platform]
output = container.call(["dpkg", "--print-architecture"], capture_output=True)
output_map_dpkg = {
OCIPlatform.i386: "i386",
OCIPlatform.AMD64: "amd64",
OCIPlatform.ARMV7: "armhf",
OCIPlatform.ARM64: "arm64",
OCIPlatform.PPC64LE: "ppc64el",
OCIPlatform.S390X: "s390x",
}
assert output_map_dpkg[platform] == output.strip()
@pytest.mark.parametrize(
("engine_name", "version", "context"),
[
(
"docker",
None, # 17.12.1-ce does supports "docker version --format '{{json . }}'" so a version before that
pytest.raises(OCIEngineTooOldError),
),
(
"docker",
'{"Client":{"Version":"19.03.15","ApiVersion": "1.40"},"Server":{"ApiVersion": "1.40"}}',
pytest.raises(OCIEngineTooOldError),
),
(
"docker",
'{"Client":{"Version":"20.10.0","ApiVersion":"1.41"},"Server":{"ApiVersion":"1.41"}}',
nullcontext(),
),
(
"docker",
'{"Client":{"Version":"24.0.0","ApiVersion":"1.43"},"Server":{"ApiVersion":"1.43"}}',
nullcontext(),
),
(
"docker",
'{"Client":{"ApiVersion":"1.43"},"Server":{"ApiVersion":"1.30"}}',
pytest.raises(OCIEngineTooOldError),
),
(
"docker",
'{"Client":{"ApiVersion":"1.30"},"Server":{"ApiVersion":"1.43"}}',
pytest.raises(OCIEngineTooOldError),
),
("podman", '{"Client":{"Version":"5.2.0"},"Server":{"Version":"5.1.2"}}', nullcontext()),
("podman", '{"Client":{"Version":"4.9.4-rhel"}}', nullcontext()),
(
"podman",
'{"Client":{"Version":"5.2.0"},"Server":{"Version":"2.1.2"}}',
pytest.raises(OCIEngineTooOldError),
),
(
"podman",
'{"Client":{"Version":"2.2.0"},"Server":{"Version":"5.1.2"}}',
pytest.raises(OCIEngineTooOldError),
),
("podman", '{"Client":{"Version":"3.0~rc1-rhel"}}', nullcontext()),
("podman", '{"Client":{"Version":"2.1.0~rc1"}}', pytest.raises(OCIEngineTooOldError)),
],
)
def test_engine_version(engine_name, version, context, monkeypatch):
def mockcall(*args, **kwargs):
if version is None:
raise subprocess.CalledProcessError(1, " ".join(str(arg) for arg in args))
return version
monkeypatch.setattr(cibuildwheel.oci_container, "call", mockcall)
engine = OCIContainerEngineConfig.from_config_string(engine_name)
with context:
_check_engine_version(engine)
+15 -13
View File
@@ -12,6 +12,7 @@ import pytest
from cibuildwheel import linux, util from cibuildwheel import linux, util
from cibuildwheel.__main__ import main from cibuildwheel.__main__ import main
from cibuildwheel.oci_container import OCIPlatform
ALL_IDS = { ALL_IDS = {
"cp36", "cp36",
@@ -26,6 +27,7 @@ ALL_IDS = {
"pp38", "pp38",
"pp39", "pp39",
"pp310", "pp310",
"pp311",
} }
@@ -73,7 +75,7 @@ def test_build_default_launches(monkeypatch):
kwargs = build_in_container.call_args_list[0][1] kwargs = build_in_container.call_args_list[0][1]
assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == {f"{x}-manylinux_x86_64" for x in ALL_IDS} assert identifiers == {f"{x}-manylinux_x86_64" for x in ALL_IDS}
@@ -81,7 +83,7 @@ def test_build_default_launches(monkeypatch):
kwargs = build_in_container.call_args_list[1][1] kwargs = build_in_container.call_args_list[1][1]
assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"] assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS} assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS}
@@ -89,7 +91,7 @@ def test_build_default_launches(monkeypatch):
kwargs = build_in_container.call_args_list[2][1] kwargs = build_in_container.call_args_list[2][1]
assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == { assert identifiers == {
@@ -99,7 +101,7 @@ def test_build_default_launches(monkeypatch):
kwargs = build_in_container.call_args_list[3][1] kwargs = build_in_container.call_args_list[3][1]
assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"] assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x} assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x}
@@ -142,7 +144,7 @@ before-all = "true"
kwargs = build_in_container.call_args_list[0][1] kwargs = build_in_container.call_args_list[0][1]
assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == {"cp36-manylinux_x86_64"} assert identifiers == {"cp36-manylinux_x86_64"}
@@ -151,30 +153,30 @@ before-all = "true"
kwargs = build_in_container.call_args_list[1][1] kwargs = build_in_container.call_args_list[1][1]
assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/manylinux2014_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == { assert identifiers == {
f"{x}-manylinux_x86_64" f"{x}-manylinux_x86_64"
for x in ALL_IDS for x in ALL_IDS
- {"cp36", "cp310", "cp311", "cp312", "cp313", "pp37", "pp38", "pp39", "pp310"} - {"cp36", "cp310", "cp311", "cp312", "cp313", "pp37", "pp38", "pp39", "pp310", "pp311"}
} }
assert kwargs["options"].build_options("cp37-manylinux_x86_64").before_all == "" assert kwargs["options"].build_options("cp37-manylinux_x86_64").before_all == ""
kwargs = build_in_container.call_args_list[2][1] kwargs = build_in_container.call_args_list[2][1]
assert "quay.io/pypa/manylinux_2_28_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/manylinux_2_28_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == { assert identifiers == {
f"{x}-manylinux_x86_64" f"{x}-manylinux_x86_64"
for x in ["cp310", "cp311", "cp312", "cp313", "pp37", "pp38", "pp39", "pp310"] for x in ["cp310", "cp311", "cp312", "cp313", "pp37", "pp38", "pp39", "pp310", "pp311"]
} }
kwargs = build_in_container.call_args_list[3][1] kwargs = build_in_container.call_args_list[3][1]
assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"] assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS} assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS}
@@ -182,7 +184,7 @@ before-all = "true"
kwargs = build_in_container.call_args_list[4][1] kwargs = build_in_container.call_args_list[4][1]
assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/musllinux_1_1_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == { assert identifiers == {
@@ -192,7 +194,7 @@ before-all = "true"
kwargs = build_in_container.call_args_list[5][1] kwargs = build_in_container.call_args_list[5][1]
assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"] assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert not kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == { assert identifiers == {
f"{x}-musllinux_x86_64" for x in ALL_IDS - {"cp36", "cp37", "cp38", "cp39"} if "pp" not in x f"{x}-musllinux_x86_64" for x in ALL_IDS - {"cp36", "cp37", "cp38", "cp39"} if "pp" not in x
@@ -201,7 +203,7 @@ before-all = "true"
kwargs = build_in_container.call_args_list[6][1] kwargs = build_in_container.call_args_list[6][1]
assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"] assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"]
assert kwargs["container"]["cwd"] == PurePosixPath("/project") assert kwargs["container"]["cwd"] == PurePosixPath("/project")
assert kwargs["container"]["enforce_32_bit"] assert kwargs["container"]["oci_platform"] == OCIPlatform.i386
identifiers = {x.identifier for x in kwargs["platform_configs"]} identifiers = {x.identifier for x in kwargs["platform_configs"]}
assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x} assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x}
+51 -8
View File
@@ -15,6 +15,7 @@ from cibuildwheel.options import (
Options, Options,
_get_pinned_container_images, _get_pinned_container_images,
) )
from cibuildwheel.util import EnableGroups
PYPROJECT_1 = """ PYPROJECT_1 = """
[tool.cibuildwheel] [tool.cibuildwheel]
@@ -178,7 +179,7 @@ def test_toml_environment_evil(tmp_path, env_var_value):
(r'TEST_VAR="before\\$after"', "before$after"), (r'TEST_VAR="before\\$after"', "before$after"),
], ],
) )
def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value): def test_toml_environment_quoting(tmp_path: Path, toml_assignment: str, result_value: str) -> None:
args = CommandLineArguments.defaults() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
@@ -255,8 +256,12 @@ def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value)
], ],
) )
def test_container_engine_option( def test_container_engine_option(
tmp_path: Path, toml_assignment, result_name, result_create_args, result_disable_host_mount tmp_path: Path,
): toml_assignment: str,
result_name: str,
result_create_args: tuple[str, ...],
result_disable_host_mount: bool,
) -> None:
args = CommandLineArguments.defaults() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
@@ -326,7 +331,9 @@ def test_environment_pass_references():
), ),
], ],
) )
def test_build_frontend_option(tmp_path: Path, toml_assignment, result_name, result_args): def test_build_frontend_option(
tmp_path: Path, toml_assignment: str, result_name: str, result_args: list[str]
) -> None:
args = CommandLineArguments.defaults() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
@@ -350,7 +357,7 @@ def test_build_frontend_option(tmp_path: Path, toml_assignment, result_name, res
assert parsed_build_frontend is None assert parsed_build_frontend is None
def test_override_inherit_environment(tmp_path: Path): def test_override_inherit_environment(tmp_path: Path) -> None:
args = CommandLineArguments.defaults() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
@@ -385,7 +392,7 @@ def test_override_inherit_environment(tmp_path: Path):
} }
def test_override_inherit_environment_with_references(tmp_path: Path): def test_override_inherit_environment_with_references(tmp_path: Path) -> None:
args = CommandLineArguments.defaults() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
@@ -434,7 +441,7 @@ def test_override_inherit_environment_with_references(tmp_path: Path):
) )
def test_free_threaded_support( def test_free_threaded_support(
tmp_path: Path, toml_assignment: str, env: dict[str, str], expected_result: bool tmp_path: Path, toml_assignment: str, env: dict[str, str], expected_result: bool
): ) -> None:
args = CommandLineArguments.defaults() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
@@ -448,4 +455,40 @@ def test_free_threaded_support(
) )
) )
options = Options(platform="linux", command_line_arguments=args, env=env) options = Options(platform="linux", command_line_arguments=args, env=env)
assert options.globals.build_selector.free_threaded_support is expected_result if expected_result:
assert EnableGroups.CPythonFreeThreading in options.globals.build_selector.enable
else:
assert EnableGroups.CPythonFreeThreading not in options.globals.build_selector.enable
@pytest.mark.parametrize(
("image", "deprecated"),
[
("manylinux1", True),
("manylinux2010", True),
("manylinux2014", False),
("manylinux_2_24", True),
("manylinux_2_28", False),
("manylinux_2_34", False),
("musllinux_1_1", True),
("musllinux_1_2", False),
],
)
def test_deprecated_image(image: str, deprecated: bool, capsys: pytest.CaptureFixture[str]) -> None:
args = CommandLineArguments.defaults()
env = {
"CIBW_ARCHS": "x86_64",
"CIBW_MANYLINUX_X86_64_IMAGE": image if image.startswith("manylinux") else "",
"CIBW_MUSLLINUX_X86_64_IMAGE": image if image.startswith("musllinux") else "",
}
options = Options(platform="linux", command_line_arguments=args, env=env)
bo = options.build_options(None)
images = bo.manylinux_images if image.startswith("manylinux") else bo.musllinux_images
assert images is not None
resolved_image = images["x86_64"]
captured = capsys.readouterr()
if deprecated:
assert f"Deprecated image {image!r}" in captured.err
assert f"{resolved_image!r}" in captured.err
else:
assert "Deprecated image" not in captured.err

Some files were not shown because too many files have changed in this diff Show More