Compare commits

..
589 Commits
Author SHA1 Message Date
Joe Rickerby 8d2b08b684 Bump version: v3.4.1 2026-04-02 14:57:41 +01:00
Matthieu Darbois 54b8a010d0 deprecation: cp313t (#2787)
Free-Threading Python 3.13 was experimental.
Now that Python 3.14 has been released with explicit support, we can schedule removal of Python 3.13 free-threading.
2026-04-02 14:39:47 +01:00
Henry Schreinerandpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 097806b6b1 tests: fully type the test suite (#2794)
* tests: fully type the test suite

* chore: require more typing

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

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

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

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-04-01 15:23:02 +01:00
Henry SchreinerandJoe Rickerby 643b30c796 fix: avoid PYTHON_VERSION breaking uv if set (#2795)
* fix: avoid PYTHON_VERSION breaking uv if set

* fix: also keep UV_PYTHON out

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* fix: specify the python version in the uv command more often

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* fix: revert env order change

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* Update windows.py

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

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2026-03-28 11:28:39 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fffe2ca07d chore(deps): bump j178/prek-action from 1.1.1 to 2.0.0 in the actions group (#2802)
chore(deps): bump j178/prek-action in the actions group

Bumps the actions group with 1 update: [j178/prek-action](https://github.com/j178/prek-action).


Updates `j178/prek-action` from 1.1.1 to 2.0.0
- [Release notes](https://github.com/j178/prek-action/releases)
- [Commits](https://github.com/j178/prek-action/compare/0bb87d7f00b0c99306c8bcb8b8beba1eb581c037...79f765515bd648eb4d6bb1b17277b7cb22cb6468)

---
updated-dependencies:
- dependency-name: j178/prek-action
  dependency-version: 2.0.0
  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>
2026-03-28 11:28:12 -04:00
611194896a fix: zizmor "code injection via template expansion" (#2784)
* Refactor action.yml to avoid template expansion, composing command line in Python

* Remove more template expansion

* Make a string quoting that's compatible with pwsh

* 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>
2026-03-27 11:49:38 -04:00
Henry Schreiner e478767d76 chore: remove some string types (#2798) 2026-03-27 08:43:58 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> caf433b137 [Bot] Update dependencies (#2789)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2026-03-24 23:52:21 -04:00
Henry Schreiner a257a3f789 chore: remove remaining future annotations (#2799) 2026-03-24 23:52:02 -04:00
Henry Schreiner 6df84da3e7 chore: some cleanup and checks (#2792)
* chore: clean up config a bit

* chore: add an extra check

* Apply suggestions from code review

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2026-03-24 23:51:49 -04:00
Agriya Khetarpal 5d52226105 ci: pin GitHub Actions + pre-commit hooks (#2744)
* Update and pin GitHub Actions

* Pin pre-commit hooks too

* adjustments (don't pin official actions)
2026-03-24 18:03:54 -04:00
Copilothenryiiicopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
817878a5f9 feat: print selected build identifiers in preamble before build loop (#2785)
* Initial plan

* Print build identifier list in preamble before build loop

Co-authored-by: henryiii <4616906+henryiii@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: henryiii <4616906+henryiii@users.noreply.github.com>
2026-03-24 17:47:41 -04:00
Henry Schreiner cb1fdd0316 chore: use absolute imports (#2796)
chore: use abosolute imports

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
2026-03-24 09:20:41 -04:00
sedat4ras 8a2ca0ce78 docs: clarify that v3 major tag doesn't exist, minor version tags do (#2790) 2026-03-23 11:43:29 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 31f0d8a0db [Bot] Update dependencies (#2783)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2026-03-18 06:56:31 +01:00
Joe Rickerby 35452dcd42 Add zizmor as a CI check (#2776)
* Add zizmor as a CI check

I'm curious about adding this tool, so experimenting with it here...

See #2770 for the impetus.

* Lie about a tag to see what happens

* Revert "Lie about a tag to see what happens"

This reverts commit f0e78b8c92c2d00f71d44e83d7904d8728427341.
2026-03-16 19:55:13 +00:00
Joe Rickerby 741be9402c Add GHA environment to update-dependencies workflow (#2778)
* Add GHA environment to update-dependencies workflow

See #2770

* activate the create-pull-request action to see if it works

* Revert "activate the create-pull-request action to see if it works"

This reverts commit b44f94d7a5c852b93b16157831b698f9c250628d.

* Only request the environment when we need it
2026-03-16 19:47:36 +00:00
Agriya Khetarpal a94d4faa11 Drop explicit build and virtualenv installation from Pyodide build tools (#2782) 2026-03-17 01:16:09 +05:30
Joe Rickerby fd19ae7e2f Merge branch '2.x' 2026-03-16 19:45:37 +00:00
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
Henry Schreiner fd6b413212 tests: use pytest 9+ and modern config (#2777)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2026-03-13 20:44:48 +01:00
Henry Schreiner 4d4daefbd2 fix: give uv the full python path (#2774)
* fix: give uv the full python path

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

* Fix python binary path in Linux build script

I thought the dir was enough. Seems not.

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2026-03-13 11:11:25 -04:00
Clément Robert 42c502093f SEC: add a 7 days cooldown period to dependabot settings (#2766) 2026-03-12 21:53:23 +00:00
Clément Robert bc6f42d805 SEC: disable default gha permissions (#2771) 2026-03-12 01:15:38 +05:30
Clément Robert eaf116e303 SEC: avoid leaking credentials in GHA (#2769) 2026-03-11 19:50:11 +05:30
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
02c132e91e [pre-commit.ci] pre-commit autoupdate (#2764)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.4 → v0.15.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.4...v0.15.5)
- [github.com/codespell-project/codespell: v2.4.1 → v2.4.2](https://github.com/codespell-project/codespell/compare/v2.4.1...v2.4.2)

* Add autoupdate schedule to pre-commit config

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2026-03-10 18:36:10 -04:00
Matthieu Darbois fd27532d12 fix: call repair-wheel-command in iOS builds (#2761) 2026-03-09 15:56:45 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1313a80d3a chore(deps): bump docker/setup-qemu-action from 3 to 4 in the actions group (#2763)
chore(deps): bump docker/setup-qemu-action in the actions group

Bumps the actions group with 1 update: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action).


Updates `docker/setup-qemu-action` from 3 to 4
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  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>
2026-03-09 15:56:18 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 601935bfc3 [Bot] Update dependencies (#2762)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2026-03-09 12:45:50 -04:00
Joe Rickerby 880a211a08 Add missing PR reference to changelog 2026-03-05 09:03:18 +00:00
Joe Rickerby ee02a1537c Bump version: v3.4.0 2026-03-05 09:00:28 +00:00
Henry Schreiner f08ce70d78 chore: match copyright to BSD-2-clause template (#2758)
See #2757.
2026-03-03 09:51:22 +00:00
Gyeongjae Choi 95b4b79b96 Fix incorrect document regarding pyodide auditwheel (#2752)
* Fix incorrect document for pyodide

* Fix incorrect document for pyodide

* Fix test
2026-03-03 16:55:56 +09:00
f046d0a94d Bump to Pyodide v0.29.3 (#2743)
* Bump to Pyodide v0.29.3

* Bump constraints to fix virtualenv release deletion

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2026-03-02 17:00:30 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f5a31ee79d chore(deps): bump the actions group across 1 directory with 4 updates (#2755)
Bumps the actions group with 4 updates in the / directory: [actions/download-artifact](https://github.com/actions/download-artifact), [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [wntrblm/nox](https://github.com/wntrblm/nox).


Updates `actions/download-artifact` from 7 to 8
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

Updates `actions/attest-build-provenance` from 3.2.0 to 4.1.0
- [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/96278af6caaf10aea03fd8d33a09a777ca52d62f...a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32)

Updates `actions/upload-artifact` from 6 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

Updates `wntrblm/nox` from 2025.11.12 to 2026.02.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/2025.11.12...2026.02.09)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/attest-build-provenance
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: wntrblm/nox
  dependency-version: 2026.02.09
  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>
2026-03-02 16:31:23 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 35ec4565f5 [pre-commit.ci] pre-commit autoupdate (#2756)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.2 → v0.15.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.2...v0.15.4)
- [github.com/python-jsonschema/check-jsonschema: 0.36.2 → 0.37.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.36.2...0.37.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-02 16:31:08 -05:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>Joe Rickerby
51fed90270 [Bot] Update dependencies (#2734)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2026-03-02 14:15:58 +00:00
Joe Rickerbyjoerickcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>Copilot
b70562f37a Debug GraalPy/uv test failures on Windows (#2741)
* Dont attempt uv frontend tests on gp311.

* docs: note GraalPy 3.11 incompatibility with uv on Windows (#2751)

* Initial plan

* docs: note GraalPy311 incompatibility with uv/build[uv] on Windows

Co-authored-by: joerick <1244307+joerick@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joerick <1244307+joerick@users.noreply.github.com>

* Skip both graalpy with uv for now

* Docs updates

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joerick <1244307+joerick@users.noreply.github.com>
2026-03-02 11:24:40 +00:00
Joe Rickerby 0a85e29549 Work around CDN rate limiting on Python.org in bin/update_pythons.py (#2753) 2026-03-01 22:51:53 +00:00
Colin Gravill 2d33864baf Pin the actions/setup-python for the externally used action.yml (#2749)
Pin the actions/setup-python to a known release
2026-03-01 22:05:39 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ade95b7be4 [pre-commit.ci] pre-commit autoupdate (#2746)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.0 → v0.15.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.0...v0.15.2)
- [github.com/python-jsonschema/check-jsonschema: 0.36.1 → 0.36.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.36.1...0.36.2)

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-01 22:02:36 +00:00
Henry Schreiner bb153041f0 fix: support -q for build/uv (#2737) 2026-02-13 20:47:03 -05:00
27fd6ecc3c docs: fix TOML reference links (#2740)
* docs: fix TOML reference links

* Apply suggestions from code review

---------

Co-authored-by: Louis Shawn <louis.shawn@qq.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2026-02-13 21:04:38 +00:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
57e8757899 [pre-commit.ci] pre-commit autoupdate (#2739)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.14 → v0.15.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.14...v0.15.0)

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

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

* Refactor test_command checks to use set syntax

* Apply suggestion from @henryiii

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2026-02-13 11:24:27 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8879b0f013 chore(deps): bump the actions group with 2 updates (#2738)
Bumps the actions group with 2 updates: [actions/download-artifact](https://github.com/actions/download-artifact) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/download-artifact` from 5 to 7
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v7)

Updates `actions/attest-build-provenance` from 3.1.0 to 3.2.0
- [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/00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8...96278af6caaf10aea03fd8d33a09a777ca52d62f)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/attest-build-provenance
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-09 17:17:39 -05:00
Henry Schreiner 9d4c80f487 docs: update for uv backend (#2736) 2026-02-08 08:35:24 -05:00
Henry Schreiner e0af304d30 fix: support uv on android (#2733) 2026-02-08 08:35:05 -05:00
Joe RickerbyandHenry Schreiner a2f965f62f Add an integration test and debug issues with the meson backend (#2718)
* Add an integration test with the meson backend

* Modify script to allow testing of the GHA action on a PR

* Try adding cython to languages in meson config

* Revert "Try adding cython to languages in meson config"

This reverts commit 50378a1c7e38665492ad0c683b178e4d96928e1e.

* Pass --vsenv to meson on windows

As seen here https://github.com/matplotlib/matplotlib/blob/9957c394bd01deb7a9bd9cb27804f447a52dc522/.github/workflows/cibuildwheel.yml#L114

* Disable win32 builds for the meson test

* Move the windows-specific config into the test project definition

This is so it can be tested with bin/run_example_ci_configs.py

* Add some docs to the FAQ about meson on windows

* Update bin/run_example_ci_configs.py

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

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2026-02-08 10:36:47 +00:00
Henry Schreiner 0d2547bf6f fix: consistently use find_uv for our usage of uv (#2730)
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
2026-02-06 15:50:18 -05:00
Joe Rickerby 1252e6249a test: try reduce flakiness of test_ssl (#2735)
test: replace badssl.com with google.com/generate_204 in SSL test

Switch to a more reliable HTTPS endpoint and add retry logic with
exponential backoff to reduce flaky test failures.
2026-02-06 15:35:20 -05:00
Agriya KhetarpalandJoe Rickerby 42d1caec22 Add a small note about security implications of using cibuildwheel in CI runners (#2711)
* Bump to actions/checkout v6 everywhere

* Add a note about security

* Not persisting credentials is still valuable

* Fix bad indentation

* Improve wording, remove link to PyPA guides

Co-Authored-By: Joe Rickerby <1244307+joerick@users.noreply.github.com>

---------

Co-authored-by: Joe Rickerby <1244307+joerick@users.noreply.github.com>
2026-02-06 18:54:08 +00:00
Henry Schreiner d31248fdbf ci: avoid Intel GraalPy updates (#2732)
* ci: avoid broken GraalPy release

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* ci: skip GraalPy when no release url available

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
2026-02-05 18:01:29 -05:00
Henry Schreiner d7dbf5a358 feat: option to build directly with uv (#2322)
* feat: option to build directly with uv

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

tests: add uv to tests

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

fix: uv doesn't pick special Pythons at the top of the path

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

fix: set uv build constraints too

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

fix: uv doesn't support PyPy 3.8

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

tests: add pyproject.toml for failing test

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

Revert "tests: add pyproject.toml for failing test"

This reverts commit 619bde368a67dfdd8f02dc33a1525c27bc8cbe91.

tests: skip uv output test

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

* tests: uv was in the wrong fixture

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* fix: from @trim21

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* tests: improve skipping

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* fix: some fixes from copilot review

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
2026-02-05 12:08:36 -05:00
Joe Rickerby 6088ea2c85 Fix the PATH mangling in action.yml (#2723)
* Fix the PATH mangling in action.yml

Let's put the `uv` on PATH question aside, and fix #2663.

This change ensures that the PATH entries added by Github when you request `shell: bash` don't make it into the pwsh environment. It also keeps the uv binary on PATH if installed via `extras:`.

Would love to get this fixed soon, then we can figure out the right approach regarding `uv` discovery.

* Modify script to allow testing of the GHA action on a PR

Cherry-picked from #2718 and modified so we can test the action.yml file
2026-02-04 09:23:27 +00:00
Andrew Murray be32c5a2ed Update README actions (#2724) 2026-01-31 13:13:32 -05:00
Henry Schreiner e8646fb903 fix: nicer fail message when --only is wrong (#2722)
* fix: nicer fail message when --only is wrong

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* Update cibuildwheel/__main__.py

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
2026-01-30 16:24:57 +00:00
Matthieu Darbois 6f8121fa47 ci: test action with uv extra (#2712)
* ci: test action with `uv` extra

* fix: uv extra for Android on Linux
2026-01-28 10:16:54 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 7989c25bcd [pre-commit.ci] pre-commit autoupdate (#2720)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.13 → v0.14.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.13...v0.14.14)
- [github.com/python-jsonschema/check-jsonschema: 0.36.0 → 0.36.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.36.0...0.36.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-01-27 09:22:47 +00:00
Agriya KhetarpalandGyeongjae Choi 9a15d0851e Bump to Pyodide v0.29.2 (#2719)
* Bump to Pyodide v0.29.2

* Bump the Pyodide constraints too

* Include new auditwheel-emscripten and pyodide-build release

* Bump pyodide-build in test to a newer version

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

---------

Co-authored-by: Gyeongjae Choi <def6488@gmail.com>
2026-01-27 09:22:16 +00:00
riri dc4fc9c3fe feat(android): add Rust cross-compilation setup for Android environment (#2650) 2026-01-25 14:13:03 +00:00
Joe Rickerby dcd9f40825 Update Python version range to 3.14 in Github Action (#2716) 2026-01-25 09:14:13 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 90da3e21e2 [pre-commit.ci] pre-commit autoupdate (#2714)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.10 → v0.14.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.10...v0.14.13)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-01-19 19:10:45 +00:00
Joe Rickerby f3ba8a83a5 Merge branch '3.3.x' 2026-01-05 20:04:05 +00:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 3cefb9d38c [Bot] Update dependencies (#2709)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2026-01-05 19:59:47 +00:00
Joe Rickerby 298ed2fb2c Bump version: v3.3.1 2026-01-05 19:57:01 +00:00
Joe Rickerbyandmayeut f0ff944318 [3.3.x] Update dependencies (#2708)
* Update deps

nox --force-color -s update_constraints && nox --force-color -s update_pins

* update deps

nox -s update_pins

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2026-01-05 19:51:01 +00:00
Xeonacid aae69a8e1d break: drop unused riscv64 enable option (#2525) 2026-01-04 20:20:29 +01:00
Matthieu Darbois 24bade1d15 break: remove Travis CI support (#2682)
Stop testing on Travis CI.
References to Travis CI are removed from docs.
Travis CI specifics are kept as-is in cibuildwheel itself (mostly logging stuff).
2025-12-29 17:03:03 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> a68c3b27fb [Bot] Update dependencies (#2704)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-12-29 17:01:45 -05:00
Henry Schreiner 99059b7442 chore: use prek in noxfile/ci (#2681) 2025-12-23 18:51:19 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> d0e070e278 [Bot] Update dependencies (#2697)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-12-23 06:44:34 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> bb2f93202e chore(deps): bump actions/attest-build-provenance from 3.0.0 to 3.1.0 in the actions group (#2698)
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 3.0.0 to 3.1.0
- [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/977bb373ede98d70efdf65b84cb5f73e068dcc2a...00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 06:44:14 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 11597ed249 [pre-commit.ci] pre-commit autoupdate (#2699)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.9 → v0.14.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.9...v0.14.10)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-12-23 06:43:57 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 90422f1824 [Bot] Update dependencies (#2692)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-12-15 23:38:01 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 986d80b3d7 [pre-commit.ci] pre-commit autoupdate (#2694)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.8 → v0.14.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.8...v0.14.9)
- [github.com/pre-commit/mirrors-mypy: v1.19.0 → v1.19.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.19.0...v1.19.1)
- [github.com/python-jsonschema/check-jsonschema: 0.35.0 → 0.36.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.35.0...0.36.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-12-15 23:37:41 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5acadc2dfc chore(deps): bump the actions group with 3 updates (#2693)
Bumps the actions group with 3 updates: [actions/download-artifact](https://github.com/actions/download-artifact), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).


Updates `actions/download-artifact` from 6 to 7
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

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

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: peter-evans/create-pull-request
  dependency-version: '8'
  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>
2025-12-15 20:31:18 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 6878a1baeb [Bot] Update dependencies (#2688)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-12-09 00:11:05 -05:00
Malcolm Smith 7288bbed76 Update Android Python versions (#2687)
* Update Android Python versions

* Restore "free up disk space" step for non-Android jobs only
2025-12-09 00:10:36 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 0cb04c05db [pre-commit.ci] pre-commit autoupdate (#2689)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.7 → v0.14.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.7...v0.14.8)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-12-09 00:09:16 -05:00
Marco Sulla 3c2d5961de Added more infos about muslinux support of uv in options.md (#2675)
uv doesn't support also ppc64le and riscv64 for muslinux
2025-12-06 12:15:11 +01:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
6c7e3ee860 [pre-commit.ci] pre-commit autoupdate (#2680)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.6 → v0.14.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.6...v0.14.7)
- [github.com/pre-commit/mirrors-mypy: v1.18.2 → v1.19.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.18.2...v1.19.0)

* chore: update for newer mypy

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryfs@princeton.edu>
2025-12-02 18:06:04 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> df2ce29908 [Bot] Update dependencies (#2677)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-12-02 12:22:25 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 74a3c45b74 [pre-commit.ci] pre-commit autoupdate (#2674)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.5 → v0.14.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.5...v0.14.6)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-11-28 15:52:36 +00:00
Eric Larson 9f991f2330 DOC: Document location in error message (#2669) 2025-11-28 15:48:44 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ea4b3eb6a5 chore(deps): bump actions/checkout from 5 to 6 in the actions group (#2672)
Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  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>
2025-11-24 11:28:39 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 6bc6700ab3 [Bot] Update dependencies (#2671)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-11-24 10:56:53 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> c314f9f07b [Bot] Update dependencies (#2665)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-11-18 09:48:55 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dd81fa002c chore(deps): bump wntrblm/nox from 2025.10.16 to 2025.11.12 in the actions group (#2666)
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 2025.10.16 to 2025.11.12
- [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/2025.10.16...2025.11.12)

---
updated-dependencies:
- dependency-name: wntrblm/nox
  dependency-version: 2025.11.12
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 09:48:43 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 3658bf2f84 [pre-commit.ci] pre-commit autoupdate (#2667)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.4 → v0.14.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.4...v0.14.5)
- [github.com/python-jsonschema/check-jsonschema: 0.34.1 → 0.35.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.34.1...0.35.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-11-17 17:51:47 -05:00
Joe Rickerby 63fd63b352 Bump version: v3.3.0 2025-11-12 19:27:31 +00:00
Henry Schreiner f4fe311fc9 fix: support Docker 29 (#2660)
* fix: support Docker 29

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

* refactor: closer to actual case, lambda

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-11-12 14:15:10 -05:00
Alexander CondelloandJoe Rickerby f6c810852d feat: make the {project} placeholder available to repair-wheel-command (#2589)
* Make the {project} placeholder available to repair-wheel-command

* Add separate test for {project} placeholder in repair-wheel-command

* Make the {project} placeholder available to repair-wheel-command

* reduce duplicated work in tests

* Use a valid wheel filename for the post-repair name

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-11-10 20:59:32 -05:00
Henry Schreiner ccbae30b53 feat: support uv with Android (#2587) 2025-11-10 19:43:52 -05:00
Henry Schreiner 1337e50f74 chore: pytest log_level is better than log_cli_level (#2657)
Committed via https://github.com/asottile/all-repos
2025-11-10 22:47:32 +01:00
Matthieu Darbois 720f8e266f feat: add manylinux_2_35 (#2656)
pypa/manylinux now has a manylinux_2_35_armv7l image allowing for better C++20 support.
piwheels is still building for Debian Bullseye so keeping manylinux_2_31 as the default image.
2025-11-10 15:54:53 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 4c7f3696b1 [pre-commit.ci] pre-commit autoupdate (#2658)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.3 → v0.14.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.3...v0.14.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-11-10 15:53:57 -05:00
Henry Schreiner e1baa60eb0 chore: enable more Ruff checks (#2654)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-11-07 18:48:20 -05:00
Joe RickerbyandHenry Schreiner 1f2f8b28d5 fix: don't constrain build-system.requires with our dependency-versions (#2583)
* Don't constrain build-system.requires with dependency-versions

* Remove setting of VIRTUALENV_PIP (it doesn't appear to have any effect)

* Alter tests to assert the versions in before_build, not setup.py

* Skip the util-test on linux

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-11-06 17:03:00 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 8c5b02f42c [pre-commit.ci] pre-commit autoupdate (#2648)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.2 → v0.14.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.2...v0.14.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-11-06 16:49:12 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 997a6e1931 [Bot] Update dependencies (#2646)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-11-06 14:43:00 -05:00
4fe7630d9c feat: add configuration option for test executor arguments (#2636)
* Add test-execution-args option.

* Add usage of test-execution-args.

* Add CI configuration to use test-execution-args.

* Document the test-execution-args setting.

* Simplify code using or syntax instead of inline if.

Co-authored-by: Malcolm Smith <smith@chaquo.com>

* Clarified some Android-specific terminology, and added details about the default args to the test runner.

* Switch to a dict-based test-execution configuration

* Add tests for test-execution parsing.

* Add all the files before pushing...

* Add note about default Android version for testbed.

* Improve description of test-execution setting.

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

* Switch to using test-runtime.

---------

Co-authored-by: Malcolm Smith <smith@chaquo.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-11-05 16:19:41 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> c53e541c2d [Bot] Update dependencies (#2642)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-10-29 12:44:46 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 58d113ad2b [pre-commit.ci] pre-commit autoupdate (#2644)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.1 → v0.14.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.1...v0.14.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-10-29 12:44:28 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> de0262ea87 chore(deps): bump the actions group with 2 updates (#2643)
Bumps the actions group with 2 updates: [actions/download-artifact](https://github.com/actions/download-artifact) and [actions/upload-artifact](https://github.com/actions/upload-artifact).


Updates `actions/download-artifact` from 5 to 6
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

Updates `actions/upload-artifact` from 4 to 5
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  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>
2025-10-27 17:56:46 +00:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>Joe Rickerby
41f3304a86 [Bot] Update dependencies (#2633)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-10-26 17:02:37 +00:00
Ofek Lev a3af13a337 Allow installation of extras in GitHub action (#2630) 2025-10-26 13:29:26 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 168717b5c5 [pre-commit.ci] pre-commit autoupdate (#2635)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.0 → v0.14.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.0...v0.14.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-10-25 16:25:41 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a592c06add chore(deps): bump wntrblm/nox from 2025.05.01 to 2025.10.16 in the actions group (#2634)
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 2025.05.01 to 2025.10.16
- [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/2025.05.01...2025.10.16)

---
updated-dependencies:
- dependency-name: wntrblm/nox
  dependency-version: 2025.10.16
  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>
2025-10-25 16:25:00 +01:00
Henry Schreiner bbe00a21e3 chore: fix warning on pylint 4 (#2624)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-10-25 15:35:48 +01:00
Malcolm Smithandpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 362020b1c7 Update Android test-command handling (#2590)
* Update Android test-command handling

* Update to Python 3.13.8

* Update documentation and tests

* Deal with `sysconfig.get_config_var("exec_prefix")` changing in Python 3.14, and add cross venv tests

* Allow test commands starting with `python3`

* test-command cleanups

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-10-25 15:29:52 +01:00
Joe Rickerby 8165422c41 chrome: remove dependency workaround for mkdocs-macro-plugin (#2639)
chrome: remove version pin for mkdocs-macro-plugin
2025-10-25 14:40:07 +01:00
Alex WillmerandJoe Rickerby a6acd2a2d5 docs: Note the evalution order of environment variables (#2618)
* docs: Note the evalution order of environment variables

* Remove example that doesn't reinforce good practice

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

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-10-15 15:44:15 -04:00
Michael R. Crusoe 372dd68cf6 docs: working examples: pyzmq no longer uses CircleCI (#2620)
pyzmq no longer uses CircleCI

https://github.com/zeromq/pyzmq/commit/014bc3eefc8bb1f883bacc9392966022de62be30
2025-10-15 15:43:39 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> cb5496943f [Bot] Update dependencies (#2621)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-10-15 15:42:54 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ad74d64a16 chore(deps): bump astral-sh/setup-uv from 6 to 7 in the actions group (#2622)
Bumps the actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).


Updates `astral-sh/setup-uv` from 6 to 7
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v6...v7)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '7'
  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>
2025-10-15 15:42:31 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> be214efadb [pre-commit.ci] pre-commit autoupdate (#2623)
updates:
- [github.com/python-jsonschema/check-jsonschema: 0.34.0 → 0.34.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.34.0...0.34.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-10-15 15:41:27 -04:00
Joe Rickerby 9c00cb4f6b Bump version: v3.2.1 2025-10-12 09:23:50 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> ae65c7d85b [Bot] Update dependencies (#2614)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-10-12 09:15:54 +01:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
86c3857999 [pre-commit.ci] pre-commit autoupdate (#2615)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.13.2 → v0.13.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.13.2...v0.13.3)

* 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>
2025-10-08 10:51:31 -04:00
Marc Mueller 68b1a81bb2 docs: include free-threading builds in identifier list (#2617)
* doc: include free-threading builds in identifier list

* doc: update Python 3.13 as well
2025-10-08 10:51:13 -04:00
Marcelo DuarteandJoe Rickerby f131cd7935 fix: Python 3.14 on macOS requires MACOSX_DEPLOYMENT_TARGET set to 10.15 (#2613)
* Python 3.14 on macOS requires MACOSX_DEPLOYMENT_TARGET set to 10.15

* Update README.md

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-10-08 10:50:57 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 8602e864d4 [Bot] Update dependencies (#2606)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-10-02 14:41:35 -04:00
Russell Keith-Magee 8ccc2650de fix: resolve issues with macOS-15 runners (#2607)
* Update iOS support packages to use new-style Xcode runner.

* Explicitly set the Xcode version for macOS runners.

* Correct a download URL.

* Minor tweak to re-trigger CI.
2025-10-02 14:41:07 -04:00
pre-commit-ci[bot] c0c1dea51f [pre-commit.ci] pre-commit autoupdate (#2608) 2025-09-29 21:22:34 +02:00
Joe Rickerby 7c619efba9 Bump version: v3.2.0 2025-09-22 21:44:26 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> bbebb68339 [Bot] Update dependencies (#2603)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-09-22 21:25:51 +01:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Joe Rickerby
5bbeaf830f [pre-commit.ci] pre-commit autoupdate (#2604)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.13.0 → v0.13.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.13.0...v0.13.1)
- [github.com/pre-commit/mirrors-mypy: v1.18.1 → v1.18.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.18.1...v1.18.2)
- [github.com/python-jsonschema/check-jsonschema: 0.33.3 → 0.34.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.3...0.34.0)

* add requests to the docs dependencies

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-09-22 21:24:22 +01:00
Tim Felgentreff 7a67c6b5a9 Add GraalPy 25 and remove workarounds for that version. (#2597) 2025-09-22 21:23:29 +01:00
Grzegorz BokotaandJoe Rickerby b8a4fbc237 Improve information about configuration overriding (#2598)
* Improve information about configuration overriding

* remove obsolete "

* Update docs/configuration.md

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

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-09-21 10:23:45 +01:00
Agriya Khetarpal 70de033a7f Replace the macos-13 images with the macos-15-intel images (GHA) (#2601)
Replace `macos-13` images with `macos-15-intel`
2025-09-21 10:23:21 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> ce4c16f88f [Bot] Update dependencies (#2602)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-09-21 10:17:10 +01:00
Malcolm Smith 66490f0536 Add CMAKE_CROSSCOMPILING_EMULATOR to Android CMake toolchain (#2591) 2025-09-20 23:50:51 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 77b9e35d7c [pre-commit.ci] pre-commit autoupdate (#2594)
updates:
- [github.com/pre-commit/mirrors-mypy: v1.17.1 → v1.18.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.17.1...v1.18.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-16 17:20:34 +05:30
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 39e9da06c5 [Bot] Update dependencies (#2593)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-09-16 17:20:22 +05:30
Henry Schreiner 9b8ce87f0a chore: linting updates (#2585)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-09-14 19:44:45 +02:00
Joe Rickerby 3a524160c3 Fix a few bugs in run_example_ci_config tooling (#2577)
* Fix a few bugs in run_example_ci_config tooling

Github-deploy.yml had a fix to make it work out-of-the-box.

* Add flaky marker on test_ssl

(due to external internet access during the test, it does occasionally fail)
2025-09-08 17:54:55 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> c294bab5aa [pre-commit.ci] pre-commit autoupdate (#2584)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.11 → v0.12.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.11...v0.12.12)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-08 17:54:44 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5a4e49a734 chore(deps): bump the actions group across 1 directory with 2 updates (#2582)
Bumps the actions group with 2 updates in the / directory: [actions/setup-python](https://github.com/actions/setup-python) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/setup-python` from 5 to 6
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

Updates `actions/attest-build-provenance` from 2.4.0 to 3.0.0
- [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/e8998f949152b193b063cb0ec769d69d929409be...977bb373ede98d70efdf65b84cb5f73e068dcc2a)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/attest-build-provenance
  dependency-version: 3.0.0
  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>
2025-09-08 16:24:05 +01:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Joe Rickerby
4aa10a9378 [pre-commit.ci] pre-commit autoupdate (#2579)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.10 → v0.12.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.10...v0.12.11)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-09-08 16:03:20 +01:00
Malcolm Smith c8fb268a1b Android test updates (#2575)
* Install the test environment while simulating Android

* Pass build-verbosity setting through to the test runner
2025-09-08 15:58:43 +01:00
Russell Keith-Magee 841e718dee Add documentation for test dependency groups on mobile platforms. (#2574) 2025-09-08 15:58:24 +01:00
Malcolm Smith 0826975570 Android updates (#2568)
* Add Python 3.14 for Android

* Simplify GitHub Actions workflows

* Update tests

* Allow running `patchelf` even when the environment's `bin` directory is not on the PATH

* Search in both the environment's bin directory and the PATH

* Use `sysconfig.get_path`

* Fix lint warning
2025-09-08 15:58:03 +01:00
Hugo van Kemenade 3f076ba487 Move PyPy 3.10 to pypy-eol (#2521)
* Move PyPy 3.10 to pypy-eol

* Refactor PyPy selection
2025-09-08 15:57:14 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> dac57ccc94 [Bot] Update dependencies (#2581)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-09-08 14:14:49 +01:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>Joe Rickerbymayeut
1d31ef4ab2 [Bot] Update dependencies (#2578)
* Update dependencies

* Fix pytest-rerunfailures version constraint

* update docker images (remove default setuptools for GraalPy)

---------

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-09-06 15:22:22 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 03ce2f2bc7 [pre-commit.ci] pre-commit autoupdate (#2573)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.9 → v0.12.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.9...v0.12.10)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-30 20:46:43 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 6331390c93 [Bot] Update dependencies (#2572)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-08-26 08:45:04 -04:00
Russell Keith-Magee 4b92d69e1b Switch Azure Pipelines iOS image (#2563)
* Revert Azure iOS runners to macos-14.

* Add warning about known macos-15 issues on Github Actions and Azure.

* Update workflow reference with actual ticket.
2025-08-20 19:51:06 -04:00
Henry Schreiner c923d83ad9 Bump version: v3.1.4 2025-08-19 14:16:52 -04:00
Agriya Khetarpal 55ee8e49bf chore: bump to Pyodide 0.28.2 (#2562)
Bump to Pyodide 0.28.2
2025-08-19 14:14:10 -04:00
Agriya KhetarpalHenry Schreinerpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
afe486c21f feat: add a --clean-cache command to clean up locations specified at CIBW_CACHE_PATH (#2489)
* Add `clean_cache` to command-line arguments

* Add `--clean-cache` to parser

* Add logic

* Add test to clean cache when it exists

* Ad test to run cache cleaning when none exists

* Add test to run cache cleaning on unwritable path

* Use `main()` instead

* tests: move command tests

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

* Create cibuildwheel cache sentinel file

* Add sentinel to existing tests + add new test

* Add signature and switch to `CACHEDIR.TAG`

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

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

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

* Another instance to replace with `CACHEDIR.TAG`

* Verify `CACHEDIR.TAG` signature

* Simplify code branching and control flow

* Add test

* Add link to `CACHEDIR.TAG` specification

* Write with UTF-8 encoding

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

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-19 11:53:27 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 1c1ba8a22e [pre-commit.ci] pre-commit autoupdate (#2561)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.8 → v0.12.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.8...v0.12.9)
- [github.com/python-jsonschema/check-jsonschema: 0.33.2 → 0.33.3](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.2...0.33.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-19 08:44:56 -04:00
Agriya Khetarpal 33b75f491a chore: extend CODEOWNERS file to include platform-specific constraints files (#2559)
* Add more files to CODEOWNERS

* Uncomment cibuildwheel-maintainers for the entire constraints

* Core maintainers are all watching the repo anyway
2025-08-18 11:15:45 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> c59c2fe70d [Bot] Update dependencies (#2556)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-08-18 11:15:00 -04:00
Agriya Khetarpal 24b64c5cd1 chore: update Pyodide constraints (#2558)
* chore: update Pyodide constraints

* Bump pyodide-lock, pyodide-build, auditwheel-emscripten
2025-08-18 11:14:43 -04:00
mara004 6425e48fed docs: minor doc link fix (#2560)
minor doc fix (configuration.md)
2025-08-18 11:14:13 -04:00
Russell Keith-Magee 0264b4d03a Revert to macOS-14 image for ARM64 iOS testing (#2557)
* Explicitly set the Xcode version on GitHub Actions.

* Revert to macOS-14 for now.
2025-08-18 11:13:47 -04:00
Joe Rickerby 68f9762657 chore: Use the virtualenv release URL rather than the blob URL (#2555) 2025-08-14 20:44:48 +02:00
Henry Schreiner 95e827ece1 chore: color and suggest_on_error are now default (3.14rc1+) (#2554)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-08-14 20:44:05 +02:00
Henry Schreiner ff9a284f21 fix: add patchelf requirement for android (#2552)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-08-13 08:52:44 -04:00
cade3864de ci: rework tests to break out ios/android tests (#2519)
* ci: rework tests to break out ios/android tests

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

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestions from code review

* ci: fix check for wheels

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

* Update .github/workflows/test.yml

* tests: pass via flag instead of envvar

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

* tests: apply to serial/parallel

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

* tests: join together marks

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

* tests: join together marks

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

* ci: break up azure job

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

* ci: use matrix for azure pipelines

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

* Try installing Java on macos as well

* Revert "Try installing Java on macos as well"

This reverts commit aedf0f5106d2467938705b85180a0b249d49b99d.

* Try to get the most basic android test running first

* Rename platform to test_select, move headers in run_tests

Maybe it's a personal thing, but the header location was really making
me have to think more than I needed to look at that file!

* Only run docker unit tests on machines that are testing linux

* Update the flag name in azure-pipelines too

* Update azure-pipelines.yml

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-08-13 08:52:27 -04:00
Joe Rickerby 63bdd4f043 docs: Improve docs on how to skip free-threaded builds (#2549)
* Improve docs on how to skip free-threaded builds

* Add a reference to the Python Free-Threading Guide
2025-08-12 13:57:18 -04:00
Jon CrallandJoe Rickerby 5952330610 tests: fix and enable doctests (#2546)
* fix doctest

* Update cibuildwheel/oci_container.py

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

* Update cibuildwheel/oci_container.py

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

* Update cibuildwheel/oci_container.py

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

* Add xdoctest to CI

* Add doctests to run tests

* Replace test map with new native method

* Forgot key for arm64

* Allow doctests to be filtered on circleci

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-08-12 09:06:12 -04:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Joe Rickerby
8db3538534 chore(deps): bump the actions group with 2 updates (#2551)
* chore(deps): bump the actions group with 2 updates

Bumps the actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

Updates `actions/download-artifact` from 4 to 5
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update actions versions in documentation/examples

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-08-12 09:03:34 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> e5caaec292 [pre-commit.ci] pre-commit autoupdate (#2550)
updates:
- [github.com/pre-commit/pre-commit-hooks: v5.0.0 → v6.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v5.0.0...v6.0.0)
- [github.com/astral-sh/ruff-pre-commit: v0.12.7 → v0.12.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.7...v0.12.8)
- [github.com/shellcheck-py/shellcheck-py: v0.10.0.1 → v0.11.0.1](https://github.com/shellcheck-py/shellcheck-py/compare/v0.10.0.1...v0.11.0.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-12 09:02:23 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 5a1712daed [Bot] Update dependencies (#2542)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-08-12 10:27:06 +01:00
Matthieu Darbois a4c6d16811 fix: ignore empty CIBW_ARCHS (#2541) 2025-08-11 10:50:25 -04:00
Malcolm Smith 78a246a901 fix: correct Android CMAKE_FIND_ROOT_PATH_MODE_PACKAGE value (#2547)
Change Android CMAKE_FIND_ROOT_PATH_MODE_PACKAGE from ONLY to BOTH
2025-08-11 10:49:48 -04:00
Joe Rickerby 47c5a31c1b fix: pyodide build fails when dependency-versions is set (#2548)
Remove auditwheel-emscripten as a top-level dep
2025-08-11 10:48:27 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 3ebb6bd942 [pre-commit.ci] pre-commit autoupdate (#2543)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.5 → v0.12.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.5...v0.12.7)
- [github.com/pre-commit/mirrors-mypy: v1.17.0 → v1.17.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.17.0...v1.17.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-05 12:25:11 -04:00
Henry Schreiner 352e01339f Bump version: v3.1.3 2025-08-01 12:38:46 -04:00
Henry Schreiner c463e56ba2 tests: another iOS flaky spot (#2539)
* tests: another iOS flaky spot

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

* tests: missing interceptions

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-08-01 12:32:01 -04:00
Vytautas Liuolia 8c5c738023 docs(project): add Falcon to working examples (#2538) 2025-07-31 12:26:27 -04:00
Henry Schreiner feeb3992a7 tests: add flaky test handling (#2527)
* chore: add flaky test handling

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

* Update test_android.py

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-31 12:26:17 -04:00
Henry Schreiner 60b9cc95db fix: never call pip directly (#2537)
You can't change the running process in Windows. In pip 25.2, the format of the generated file changes, so this now breaks if you try to update to the latest pip (which can happen if deps are unpinned).
2025-07-31 12:26:07 -04:00
Tim Felgentreff e2c7102ed7 chore: remove some GraalPy Windows workarounds. (#2501)
GraalPy made a release where pytest works on Windows
2025-07-31 00:31:23 -04:00
Henry Schreiner 9e4e50bd76 Bump version: v3.1.2 2025-07-29 09:12:51 -06:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 8ef9414f60 [pre-commit.ci] pre-commit autoupdate (#2532)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.4 → v0.12.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.4...v0.12.5)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-28 16:06:41 -06:00
Joe Rickerby 1953c04972 Adding @mhsmith as platform maintainer for Android (#2516) 2025-07-28 13:00:54 -06:00
Russell Keith-Magee 46a6d27995 Bump iOS support package versions. (#2530) 2025-07-28 12:26:52 -06:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 0dc0b48e4d [Bot] Update dependencies (#2529)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-28 08:20:49 -06:00
Matthieu Darbois b887417e0e fix: enable all shall not enable cpython-experimental-riscv64 (#2528) 2025-07-27 23:18:35 -06:00
Matthieu Darbois 36c5079ce5 tests: reduce testing time on iOS (#2523)
A number of tests only require to run with a single version of python.
This will reduce the time needed to run those tests.
2025-07-27 23:15:55 -06:00
Matthieu Darbois 68c03665d3 feat: add deprecation warning for cpython-experimental-riscv64 enable (#2526) 2025-07-27 15:58:03 -04:00
Matthieu Darbois 1c38fac9c8 fix: invalid warning on test-skip = ["*-macosx_universal2:arm64"] (#2522) 2025-07-27 09:13:38 -04:00
Henry SchreinerandMatthieu Darbois 140bc1a96a tests: mark and add skips (#2524)
* tests: mark and add skips

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

* fix comment

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2025-07-27 12:12:42 +02:00
Henry Schreiner 55fcb25edc fix: error on CIBW_FREE_THREADED_SUPPORT set (#2520)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-26 19:09:26 +02:00
Clément Robertandmayeut c89609aaf2 BUG: fix incorrect number of wheels in logs (#2517)
* BUG: fix incorrect number of wheels in logs

* add a test

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-07-26 13:13:01 +02:00
tabedzkiHenry SchreinerJoe Rickerbypre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
13b086e3c6 docs: clarify language around Python requirements for local cibuildwheel usage (#2513)
* First set of changes to setup.md

* Update docs/setup.md

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

* Update docs/setup.md

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

* [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: Joe Rickerby <joerick@mac.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-25 00:46:16 -04:00
Malcolm Smith 302af02853 fix(android): execute wheel via sys.executable (#2515)
* Android: execute `wheel` via `sys.executable`

* Suppress unhelpful cyclic-import warning
2025-07-24 16:38:22 -04:00
Matthieu Darbois 9361af3204 feat: riscv64 builds are now enabled by default (#2509)
PyPI now accepts `riscv64` uploads for manylinux/musllinux.
Those builds do not require an opt-in anymore.
2025-07-24 14:52:07 -04:00
Malcolm Smith 47c5a35421 Add Cirrus macOS as a runner that supports building for Android but not testing (#2514)
* Add Cirrus macOS as a runner that supports building for Android but not testing

* Try to trigger Cirrus by changing .cirrus.yml
2025-07-24 14:51:39 -04:00
Joe Rickerby e6de07ed39 Bump version: v3.1.1 2025-07-24 19:14:08 +01:00
timrid 2ca692b1e5 docs: iOS typo fix in docs (#2510)
Update platforms.md

Fixed typo
2025-07-24 12:29:16 -04:00
Clément Robert 1ac7fa7f00 fix: report defects in logs and HTML summaries (#2512)
* BUG: fix incorrect reporting of number of wheels produced

* BUG: fix incorrect naming of test only sessions in html summaries
2025-07-24 12:28:30 -04:00
Henry Schreiner ffd835cef1 Bump version: v3.1.0 2025-07-23 23:23:06 -04:00
Henry Schreiner 3e2a9aa6e8 fix: regenerate schema
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-23 22:47:02 -04:00
cibuildwheel-bot[bot] 10c727eed9 feat: Python 3.14rc1 build by default (#2507) 2025-07-23 20:59:02 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
f628c9dd23 [pre-commit.ci] pre-commit autoupdate (#2505)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.3 → v0.12.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.3...v0.12.4)
- [github.com/pre-commit/mirrors-mypy: v1.16.1 → v1.17.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.1...v1.17.0)

* chore: fix typing issue from match statement

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>
2025-07-23 17:18:48 -04:00
0f487ee2cb feat: add support for building Android wheels (#2349)
* Add Android to resource files

* Add Android to miscellaneous places

* Add Android documentation

* Docs cleanups

* Add Android platform module; implement top-level structure and target Python installation

* Implement setup_env and build_wheel

* lru-dict build working

* Alter prefix in sysconfigdata file; fix various issues with FLAGS variables

* Implement Android testing

* Add type annotations to _cross_venv

* Revert Python 3.8 to pip 25.0.1

* Make test-sources required on Android

* Add Android integration tests

* Test cleanups

* Add test of all available Python versions

* Update test-sources and test-command behavior to match iOS

* Documentation cleanups

* Replace Builder class with a set of global functions

* Rename "env" to "build_env"

* Remove Chaquopy repository from default pip command line

* Move native_platform to platforms module

* Fix parse_config_settings

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

* Add unit tests for parse_config_settings and arch_synonym

* Make `shell_prepared` arguments keyword-only, and add tests for the commands that use it

* Replace `importlib.util.spec_from_file_location` with `runpy.run_path`

* Use python-build-standalone

* Update Android Python

* Enable KVM in Linux CI

* Move KVM code to test_android.py

* Use Java 17 on Azure

* Install emulator if necessary before running -accel-check

* Free up additional disk space on Linux runners

* Add sudo

* Skip emulator tests on CI platforms that don't support it

* Download Android Python from Maven Central

* Free up more disk space on Linux runners

* fix: minor fixups

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

* Set sysconfig._BASE_PREFIX to support sysconfig.get_path("include")

* Get ANDROID_API_LEVEL from the build environment, not cibuildwheel's own environment

* Correct relative path of test-sources

* Pass a CMake toolchain file to the build

* Add "repair" step which adds libc++ to the wheel when necessary

* Add missing needs_emulator decorator

* Provide useful error message if ANDROID_HOME is not set

* Remove use of HOST environment variable

* Update to Python 3.15.5

* Fix PyLint warnings, clarify comment

* Group common arguments into a dataclass

* Handle environment variables containing newlines

* Discourage the use of `pytest` test commands without `python -m`

* Use single quotes in user-visible messages

* Improve testing documentation

* Pass wheel filename to `log.build_end`

* In GitHub Actions example, skip Android tests on macOS

* Correct relative paths in `patchelf --set-rpath`

* Clarify `test-sources` docs

* Update to Python 3.13.5+20250722.214220

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-23 17:18:33 -04:00
Matthieu Darbois e2e24882d8 feat: add default riscv64 images (#2506) 2025-07-23 13:28:36 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> a8bff94dbb [Bot] Update dependencies (#2504)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-21 14:16:55 +02:00
Henry Schreiner fbf37256a5 feat: include free-threaded 3.14 by default (#2503)
This is no longer experimental in 3.13 beta 3, so we don't need an enable for it anymore. Someone can skip expliclity with `'cp31?t-*'` if they really don't want free-threaded wheels.

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-19 19:38:05 -04:00
Matthieu Darbois 9613645abc feat: add support for manylinux_2_34 i686 image (#2500) 2025-07-18 11:30:42 -07:00
Henry Schreiner 7247e3887c fix: show options for --only (#2499)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-17 00:00:49 +02:00
Henry SchreinerandJoe Rickerby d0e4aded3e feat: add summary to Action (#2469)
* chore: add summary to Action

* refactor: new summary table

* fix: fixup tests and formatting

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

* fix: pyodide missing some logging

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

* fix: nicer printout, nicer in-place summary

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

* fix: use summary for everything

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

* fix: support only one output wheel from repair

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

* Add new Github summary format

* Remove a couple of humanize uses

* fix: filter ANSI codes in summary

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

* fix: add sha256

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

* fix: nicer wheel/wheels depending on how many are present

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-07-15 20:45:53 -07:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> a2c263d4ab [pre-commit.ci] pre-commit autoupdate (#2497)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-14 21:34:17 -07:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 576ebccc78 [Bot] Update dependencies (#2496)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-14 01:31:16 -07:00
Agriya KhetarpalandHenry Schreiner cd9b48cb74 feat: Pyodide 0.28 release (#2487)
* Bump testing version to 0.28.0

* Bump Pyodide constraints

* Add Pyodide 0.28.0 identifier

* Override to a different, alpha version for testing

* Update docs for Pyodide versions everywhere

* Add comment on using older Pyodide alpha version

* Bump to `"cp313*"` for compatibility

* Drop identifier for Pyodide 0.28.0a3

* Add back Pyodide 0.28.0a3 identifier

* Add helpful comment about pyodide-prerelease option

* Fix test and add a comment about it

* Keep one Pyodide version per Python version

* Fix docs for `pyodide-prerelease`; Python ➡️ Pyodide

* Update utils.py

* Drop CI test step for overridden Pyodide version

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

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-07-12 18:38:50 -07:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 5e245905b3 [Bot] Update dependencies (#2488)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-07-09 22:14:56 -07:00
Hanlu 157d81eddb chore: remove redundant word in comment (#2492)
Signed-off-by: liangmulu <liangmulu@outlook.com>
2025-07-09 13:57:38 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 75b1860702 [pre-commit.ci] pre-commit autoupdate (#2491)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2)
- [github.com/python-jsonschema/check-jsonschema: 0.33.1 → 0.33.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.1...0.33.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-08 09:00:18 -04:00
MichaIng 807cd81699 fix: typo in riscv64 CPython 3.9 identifier (#2490)
Signed-off-by: MichaIng <micha@dietpi.com>
2025-07-08 08:59:53 -04:00
Henry SchreinerandJoe Rickerby 834ea40510 chore: pattern matching (#2434)
* chore: use match for stringify_setting

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

* chore: more match statements

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-07-08 08:59:20 -04:00
Matthieu DarboisandHenry Schreiner 46ef6044e2 feat: add support for manylinux_2_28 i686 image (#2480)
Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-07-08 09:32:51 +01:00
Henry Schreiner dafc3545eb fix: support only one output wheel from repair (#2478)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-07-08 09:32:22 +01:00
Joe Rickerby a6ec930f15 Welcome to our new platform maintainers! (#2481)
* Add new platform maintainers to README!

* Add a codeowners file
2025-07-08 09:14:34 +01:00
Joe Rickerby 95d2f3a92f Bump version: v3.0.1 2025-07-05 19:03:42 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 40de3fe510 [pre-commit.ci] pre-commit autoupdate (#2483)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.0 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.0...v0.12.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-01 16:58:23 -04:00
920081014e feat: added Python 3.14 preview for iOS (#2475)
* Added Python 3.14 preview for iOS

* Test all Python versions with iOS

* add cp314-ios to the build selector table

---------

Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-06-30 10:32:25 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 94fe0a212f [Bot] Update dependencies (#2482)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-30 10:31:30 -04:00
Henry Schreiner 405ddd5315 fix: pyodide missing some logging (#2477)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-28 09:18:18 +02:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
34b4f1e86e [pre-commit.ci] pre-commit autoupdate (#2474)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.0)
- [github.com/pre-commit/mirrors-mypy: v1.16.0 → v1.16.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.0...v1.16.1)
- [github.com/python-jsonschema/check-jsonschema: 0.33.0 → 0.33.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.33.0...0.33.1)

* chore: address new lints

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>
2025-06-24 09:48:01 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> e69b5532ab [Bot] Update dependencies (#2473)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-23 13:08:10 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 3e86452449 [Bot] Update dependencies (#2471)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-20 10:11:04 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e73749579b chore(deps): bump actions/attest-build-provenance from 2.3.0 to 2.4.0 in the actions group (#2472)
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 2.3.0 to 2.4.0
- [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/db473fddc028af60658334401dc6fa3ffd8669fd...e8998f949152b193b063cb0ec769d69d929409be)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 16:08:18 -04:00
Henry Schreiner 588dee0e0c docs: include Windows ARM in examples (#2468)
docs: include Windows ARM
2025-06-14 09:58:07 -04:00
Henry Schreiner 9c53c4ee46 chore: minor tweaks to generate schema script (#2467)
chore: minor tweeks to generate schema script

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-14 14:58:57 +02:00
Joe Rickerby 5f22145df4 Bump version: v3.0.0 2025-06-11 14:02:58 +01:00
Joe Rickerby a73177515a Docs: mobile layout fix (#2466)
* Remove console warning

* Fix table overflow issue on mobile
2025-06-11 08:57:43 +01:00
Henry Schreiner ff86a64577 docs: add tips for numpy (#2465)
* docs: add tips for numpy

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

* docs: update example for numpy 2.3.0

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

* Update docs/faq.md

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-11 08:57:24 +01:00
Henry Schreiner 6f5e480fec chore: use pip's groups in CI (#2463)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-11 08:56:12 +01:00
Henry Schreiner 3c5ff09888 Bump version: v3.0.0rc3 2025-06-11 00:57:01 -04:00
cibuildwheel-bot[bot] 1b9a56e014 [Bot] Update dependencies (#2455) 2025-06-10 21:06:51 -04:00
Henry Schreiner aa9fe2a24e ci: use uv python for docs (binary b1) (#2462)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-10 18:28:34 -04:00
Henry SchreinerandJoe Rickerby e188d9e260 feat: remove 32-bit linux from auto arch, fix auto32 on linux aarch64 (#2458)
* feat: remove 32-bit linux from auto arch, fix auto32 on linux aarch64

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

* Apply suggestions from code review

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

* Update cibuildwheel/architecture.py

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-06-10 17:57:41 -04:00
Henry Schreiner 3fa7bd1e72 ci: fix cirrus and reduce rebuilds (#2460)
* ci: fix Cirrus CI on Windows

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

* ci: skip unrelated changes more often

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-10 17:57:25 -04:00
Joe Rickerby c6368be701 Move to the OS-latest image tags on Azure Pipelines (#2461) 2025-06-10 16:48:53 -04:00
Henry Schreiner dcecda6962 chore: handle update of cog to 3.5 (#2459)
chore: handle update of cog

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-10 01:54:45 -04:00
Henry Schreiner adc58e93d1 Revert "ci: update example too"
This reverts commit 487154abbb.
2025-06-09 23:39:56 -04:00
Henry Schreiner 487154abbb ci: update example too 2025-06-09 23:15:34 -04:00
Henry Schreiner 0fe82c5882 chore: order python-standalone releases for stability (#2456)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-09 18:32:32 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 0b6c7d0aff [pre-commit.ci] pre-commit autoupdate (#2457)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.12 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.12...v0.11.13)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-09 19:37:40 +01:00
Henry Schreiner 38dab4b641 docs: warning about pyodide-version (#2450) 2025-06-06 09:57:58 -04:00
Henry Schreiner 2edf578499 Bump version: v3.0.0rc2 2025-06-06 00:05:23 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 70c6aba901 [Bot] Update dependencies (#2449)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-06 00:00:58 -04:00
Joe Rickerby 31f536bdf3 Bump version: v3.0.0rc1 2025-06-05 11:39:19 +00:00
cibuildwheel-bot[bot] 7601bfcb45 [Bot] Update dependencies (#2443) 2025-06-04 18:21:09 -04:00
Joe Rickerby b5c33b7a09 Docs fixes (#2446) 2025-06-04 18:20:54 -04:00
Henry Schreiner 4e8d0757df chore: touch up release script (#2442)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-03 17:34:22 -04:00
Henry Schreiner a76b6fc338 Bump version: v3.0.0b5 2025-06-03 02:49:08 -04:00
Henry Schreiner ef60c822df ci: fix gitlab Windows with --allow-downgrade
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-03 02:36:02 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
4aa7d7e9c1 [pre-commit.ci] pre-commit autoupdate (#2440)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.11 → v0.11.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.11...v0.11.12)
- [github.com/pre-commit/mirrors-mypy: v1.15.0 → v1.16.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.15.0...v1.16.0)

* chore: update some things for checks

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

* chore: fully use PEP 639 licensing

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>
2025-06-02 23:36:28 -04:00
Russell Keith-Magee 03a2ba0bec docs: correct documentation suggesting iOS builds all platforms by default. (#2441) 2025-06-02 23:36:14 -04:00
Joe RickerbyandHenry Schreiner 4aaa823c8e feat: add pyodide 0.28 and pyodide-prerelease (#2431)
* Add pyodide 0.28.0a3

* Add docs and tests for 0.28
This reverts commit 2e7d716a9ecfbdac159eed798ebf1b0170e6858d.

* fix: add selector for pyodide-prerelease, and add graalpy to schema

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-06-02 23:35:59 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> e6b4e2d355 [Bot] Update dependencies (#2439)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-06-02 11:37:35 -04:00
Henry Schreinerandpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> c90accef51 docs: color output for docs (#2407)
* docs: color output for docs

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

* fix: use asdf (python-build)

* docs: try to fix colors

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

* Update .pre-commit-config.yaml

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

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-06-01 01:34:20 -04:00
Joe Rickerby 2346c5f6e0 Fix changelog formatting bug 2025-06-01 06:28:20 +01:00
Henry Schreiner 2125d7117e fix: test-sources should use project dir (#2437)
* fix: test-sources should use project dir

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

* docs: fix docstring

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-31 23:36:02 -04:00
Henry SchreinerandJoe Rickerby ea08120e25 feat: support multiple commands on iOS (#2432)
* feat: support multiple commands on iOS

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

* Move split_command into a util module

* (unrelated) fix test docstring

* Implement short-circuit behaviour on test-command

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-05-30 09:53:20 -04:00
Henry SchreinerandMatthieu Darbois f8168e2a89 docs: found more to convert to toml (#2430)
* docs: found more to convert to toml

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

* Apply suggestions from code review

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2025-05-29 19:54:00 -04:00
Henry Schreiner 3431adedba fix: use standard schema line (#2433)
This was a workaround for a bug in validate-pyproject, but that bug was fixed some time ago, so we can use the standard line here now.

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-29 19:44:38 -04:00
Joe Rickerby 733573feb3 Debug Cirrus CI testing (#2414)
* Skip Cirrus CI iOS testing

* Remove support tick in readme

* Debugging cirrus windows failure

* Use a separate file to avoid error messages from vsdevcmd getting into the output

* cirrus: TEMP target just the test i care about

* Revert "cirrus: TEMP target just the test i care about"

This reverts commit 7ed4417970930ab9e0e1cb1f17497d406310d6ff.

* Remove debug code

* Fix windows issues with multiline -c Python commands
2025-05-29 10:52:46 -04:00
Henry Schreiner c5a3ced8c8 docs: rerender README
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-29 10:37:55 -04:00
Joe Rickerby 6f2d68febf Add missing changelog entries 2025-05-29 12:25:06 +01:00
Henry Schreiner e8a7a374ee docs: typo b3 -> b4 2025-05-29 00:33:43 -04:00
Henry Schreiner cf078b0954 Bump version: v3.0.0b4 2025-05-29 00:27:20 -04:00
Henry Schreiner b9f1942e8d chore: use cog for quick README updates (#2428)
* chore: use cog for quick README updates

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

* refactor: rename scripts

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-28 23:36:16 -04:00
Henry Schreiner 2d6c328457 fix: drop PYTHONSAFEPATH (#2429)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-28 19:43:19 -04:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>Henry Schreinermayeut
0e8f4f777d [Bot] Update dependencies (#2426)
* Update dependencies

* tests: ignore tag order

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

* tests: use sorted platform tags

Per PEP 425, compressed tag sets should be sorted.
This was fixed in auditwheel 6.4.0

* tests: use sorted platform tags

this shouldn't change that often, use static order to allow override.

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-05-28 19:42:00 -04:00
Joe Rickerby 7e099863cd docs: update options table in README and keep up-to-date with commit hook (#2427)
* Update options table in README and keep up-to-date with commit hook

* Separate multiple options with a newline

* remove the old javascript version
2025-05-28 15:38:53 -04:00
Joe Rickerby c41b47f7c2 Bump version: v3.0.0b3 2025-05-28 20:07:38 +01:00
Joe RickerbyandHenry Schreiner 6379772957 Revert to running tests from from a temp dir when test-sources is unset (#2420)
* Revert to running tests from from a temp dir when test-sources is unset

* Fix placeholders error message, add test for it

* Add back {project} placeholders to CIBW_TEST_COMMAND in tests & docs

* Update test/test_before_test.py

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-05-28 16:05:16 +01:00
Joe Rickerby 7298563b25 docs: fix many/musllinux option names and values and other broken links (#2425) 2025-05-28 16:04:55 +01:00
Henry Schreiner db2d59be22 chore: use kw_only (Python 3.10+) on most dataclasses (#2422)
* chore: use kw_only (Python 3.10+) on many dataclasses

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

* fix: expose windows file to type checker

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-28 10:41:26 +01:00
Henry Schreiner e18ab66cca fix: pyodide missing wheel placeholder (#2421)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-27 19:32:25 -04:00
Henry Schreiner b8814b8148 chore: fix update script to handle pymanager addition to page (#2424)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-27 19:32:14 -04:00
Joe Rickerbyandmayeut e92147f52a [travis-ci] debugging ci failures on travis (#2387)
* [travis-ci] debugging ci failures on travis

* [travis-ci] Use JSON to encode env vars

* [travis-ci] Use a more broadly compatible before_all command

* Drop CIBW_ENABLE=all on Travis windows
It doesn't fit into the time limit

* [travis-ci] try upgrading certifi to get around SSL errors

* Revert "[travis-ci] try upgrading certifi to get around SSL errors"

This reverts commit 02eee0e1946d2116d1ce23ccd86945ad33d1ae24.

* [travis-ci] upgrade windows certificates

* [travis-ci] re-enable linux builds

* Drop CIBW_ENABLE=all on Travis Linux

It doesn't fit into the time limit

---------

Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-05-26 23:44:39 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 3bbc772778 [pre-commit.ci] pre-commit autoupdate (#2417)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.10 → v0.11.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.10...v0.11.11)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-26 23:42:40 -04:00
Russell Keith-Magee 2b4a8535d9 fix: ensure that deep paths exist before copying files. (#2418) 2025-05-26 23:42:21 -04:00
Matthieu Darbois 2d6b40d669 fix: rename gp242 identifier to gp311_242 (#2411) 2025-05-26 15:49:59 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> b1d944060a [Bot] Update dependencies (#2415)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-05-26 09:42:05 -04:00
Henry Schreiner e2b6431822 docs: flip TOML and envvars order (#2389) 2025-05-26 00:35:27 -04:00
Joe Rickerby 44d6ccfade Merge remote-tracking branch 'origin/main' 2025-05-25 13:04:36 +01:00
Joe Rickerby dded469d60 Bump version: v3.0.0b2 2025-05-25 13:02:14 +01:00
Henry Schreinerandpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 1bfec0e7c9 chore: move codespell config to pyproject.toml (#2409)
* chore: move codespell config to pyproject.toml

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

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

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-25 13:58:13 +02:00
Henry Schreiner 327f5c327d ci: add labels to autoupdate (#2408)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-22 11:01:58 +01:00
Agriya KhetarpalHood ChathamJoe RickerbyJoe RickerbyHenry Schreinerpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
163864158d feat: Pyodide improvements: version setting, standalone environments (#2002)
* Fix a typo: pyoodide ➡️ pyodide

* Add `pyodide_build_version` attribute

* Add version to xbuildenv log step

* Add version to Emscripten log step

* Use `pyodide-build`'s version for updating constraints

* Bump Pyodide constraints by updating `pyodide-build`

* Add a schema for `pyodide-version`

* Update Pyodide constraints

* Bump `pyodide-build` to new 0.29.0

* Test out another Pyodide identifier

* Update outdated Pyodide constraints

* Add Pyodide version to temp directory name

* Remove Pyodide 0.26.1 from build configurations

* Retrieve + validate + install specific xbuildenvs

* Test wheel builds with Pyodide 0.26.2

* Add correct Pyodide version to identifier temp dir

* Don't pre-call Pyodide xbuildenv search

* Fetch just the stable Pyodide versions

* Refactor search + validation + install into one step

* Move all of it under a lock

* Reorder xbuildenv installation

* Add env and cwd to xbuildenv search call

* Temporarily lower to 0.26.2 target

* Separate out search, validate, install; again

* Run xbuildenv search in `CIBW_CACHE_PATH`

* Remove prior `PYODIDE_ROOT` env vars, copy envs

* Validate doesn't need to depend on searching

* Add file lock when searching xbuildenvs

* Test the original version: 0.26.1

* Update Pyodide constraints

* Update constraints for `pyodide-build` 0.29.0 again

* Bump Pyodide from version 0.26.1 ➡️ version 0.26.4

* Add note on compatibility for macOS + other archs

* Note Pyodide version for Pyodide identifier

* Docs about `CIBW_PYODIDE_VERSION`

* Don't fetch just the stable versions

* Discard a variable that's not used later

* Rename `search_xbuildenv` ➡️ `get_xbuildenv_versions`

* `validate_xbuildenv` ➡️ `validate_xbuildenv_version`

* Replace ordered comment, add newline

* Replace sentence on macOS support

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

* Capitalise: "pyodide" ➡️ "Pyodide"

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

* "work" ➡️ "may succeed"

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

* Add another job to test a custom Pyodide version

* Handle "v"-prefixed + non-prefixed versions

* Convert to a proper toml-able option, and remove some hardcoded versions

This removes the enscripten and pyodide-build version specs from
pyproject.toml - pyodide-build is spec'd in the constraints file, and
the emscripten version can be read from the pyodide-build output.

* Add a schema entry

* Add docs for CIBW_PYODIDE_VERSION

* Rephrase

* Add tests for pyodide-version

* Apply suggestions from code review

* Add python_build_standalone util

* Hook up to python-build-standalone, removing dependency on host python

* Remove python hard-code in action.yml

* Add log step

* Add workaround for https://github.com/pyodide/pyodide-build/issues/143

* Add emscripten pytest test

* Remove unneeded checks

* Fix a pytest invoke for emscripten

* Generate pyodide-build constraints from the pinned pyodide version

* Remove pyodide python-build-standalone workaround

* Fixup paths from newer version of pyodide-build

* Fix/skip some failing tests

* Use `python -m pytest` on pyodide, even on Linux

* Docs fixes

* Don't call the github API at runtime, cache the release assets instead

* Ignore pylint false positive

* Add version auto-updating for pyodide

* Add support for pyodide 3.13.

* Fix tests for multiple pyodide wheels

* Remove workaround for unreleased pyodide-build

* Rename to "test_pyodide"

* Fix pathname confusion

* Remove extra github actions job

* Fix expectation for test_abi_none

* Fix the custom_repair_wheel test to actually have clashing names

* Fix pinned version test

* Document test-command limitation

* Remove pyodide 0.28.0a1 for now

* Update constraints files

* Docs/test fixes post removing pyodide cp313

* Fix ABI test expectation

* Docs improvements

* Improve some comments

* Remove logic duplication

* remove pyodide special casing

* Refactor constraints code to use a utility script, circumventing import issues

* chore: nicer nox env

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

* fix: typo in variable name found by copilot

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

* Apply suggestions from code review

* Some more Pyodide version updates in the docs section

* We haven't released Pyodide v0.27.6 yet

* Back to the Github URL for cross-build-environments

* `pyodide-build`, not `emsdk` for Windows skips

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

* Move to a separate `_json_request` function

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

* Rename "retries" ➡️ "retry_count"

* Add some type hints

* Copy env vars before `UV_CUSTOM_COMPILE_COMMAND`

* Use `HTTPError.headers.get` instead

* Remove extra end quote

* Change download tests URL to `https://badssl.com/`

Co-Authored-By: Joe Rickerby <1244307+joerick@users.noreply.github.com>

* Download size changes, too

* Use jsdelivr for Github asset mirroring

* Bump to Pyodide v0.27.6

* Fix unit tests

* Move to pyodide v0.27.6 again

* Bump to pyodide-build 0.30.4

* Use new URL for cross-build environments metadata

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

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

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Joe Rickerby <1244307+joerick@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-20 16:59:58 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> c3c260dff7 [pre-commit.ci] pre-commit autoupdate (#2403)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.9 → v0.11.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.9...v0.11.10)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-20 13:43:19 +01:00
Joe Rickerby fcdbed860e Add TEST_ENVIRONMENT option and set PYTHONSAFEPATH=1 in the test env (#2388)
* Add TEST_ENVIRONMENT option and set PYTHONSAFEPATH=1 in the test env

Closes https://github.com/pypa/cibuildwheel/issues/2358

* Add toml docs examples

* Add TOML examples

* Use double-quotes for windows
2025-05-20 13:41:26 +01:00
Joe Rickerby 9dd7192d89 ci: fix failing tests due to rate-limiting (#2401)
raw.githubusercontent.com URLs are rate-limited now, change URLs
2025-05-19 11:58:28 -04:00
Joe Rickerby bd641d11de Add docs note 2025-05-19 10:02:39 +01:00
Joe Rickerby 90a0ddeff0 Bump version: v3.0.0b1 2025-05-19 09:29:14 +01:00
Joe Rickerby e46e9516d9 Fix release script 2025-05-19 09:06:06 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 1fe8e55c67 [Bot] Update dependencies (#2400)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-05-19 08:52:26 +01:00
Joe RickerbyandMalcolm Smith 898f0a45c1 fix: change to the iOS testing option semantics (#2363)
* Change to the iOS testing option semantics

Don't assume the presence of `python -m` in the test command. Less magic
and allows more option reuse between platforms.

* Update schema

* Add a test for this warning

* Don't try to execute a test-command when it doesn't look like a module

* Update docs/options.md

Co-authored-by: Malcolm Smith <smith@chaquo.com>

* Only allow invalid test command if the first part is 'pytest'

* Responses to code review from @freakboy3742

* Fixes post-merge

---------

Co-authored-by: Malcolm Smith <smith@chaquo.com>
2025-05-18 22:42:23 -04:00
Joe Rickerby 1bc7e904b8 Improve integration test options (#2385)
* Improve integration test options

- Keep the platform and enable options in os.environ, don't duplicate state in utils
- Provide more user-friendly options for setting enable and platform in integration tests

* Ensure that CIBW_ENABLE is set in the test process

* Use get_enable_groups() where necessary
2025-05-16 11:11:16 +01:00
Jinzhe Zeng 166465b56d doc: update Windows ARM for GH Actions (#2396)
It's no more cross compilation and can run tests.
2025-05-16 01:41:19 -04:00
Henry Schreiner c2cffa3d1e feat: adding pypy-eol enable group (#2393) 2025-05-15 09:06:48 -04:00
Henry Schreiner cab1ff4076 fix: support Python 3.14's color CLI (#2394)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-05-14 13:04:07 -04:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>Henry Schreinerpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
d193a8eff5 tests: fix ci (#2391)
* Update dependencies

* tests: disable color for Pyodide test

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

* fix: nist.gov doesn't work anymore for this test

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

* fix: limit click for typer

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

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

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-14 10:06:34 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> e7dcb31aa7 [pre-commit.ci] pre-commit autoupdate (#2392)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-13 01:28:55 -04:00
Matthieu Darbois 5622cc6988 Add Python 3.14 preview (#2390)
* feat: add CPython 3.14 beta 1

* fix tests
2025-05-12 12:54:02 -04:00
Joe Rickerby 54a4dccd62 Remove official Appveyor support (#2386) 2025-05-11 11:16:54 +02:00
Henry Schreinerandpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 5b65230f64 fix: virtualenv 20.31 no-wheel (#2382)
* fix: virtualenv 20.31 no-wheel

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

* fix: show failed output on failure

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

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

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

* Update linux.py

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

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

* Update linux.py

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

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-08 12:12:14 -04:00
Joe Rickerby 04c9427a20 Test enable groups as specified by PR labels (#2357)
* Allow CIBW_ENABLE to control the wheels built in testing

* Set CIBW_ENABLE using PR labels

* Add docs

* Build everything on the main branch

* Add CIBW_ENABLE=all option

This was mostly for use in the `main` building case, because otherwise it's maybe a bit too easy to forget to update this file when adding an enable group

* Remove dead code

* Make unit tests robust to the value of CIBW_ENABLE

* Fix tests that explicitly choose pypy

* Fix test expectation

* Don't expect impossible wheels in expected_wheels

* Simplify logic in expected_wheels

* CircleCI- run with CIBW_ENABLE=all only on the main branch

* Azure pipelines - run with CIBW_ENABLE=all on main branch

* Update gitlab to run CIBW_ENABLE=all on main

* Set CIBW_ENABLE=all on travis - it only runs on main anyway

* Fix job name error on CircleCI

* Fix tests for graalpy

* Update the test configuration to use the label

* Remove duplication of default value. Make it affect sample build too

* Move the action to after deps are installed

* GraalPy workaround for this assumption

* Make unit test resilient to changing CIBW_ENABLE
2025-05-08 12:27:05 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> edbd234b82 [pre-commit.ci] pre-commit autoupdate (#2381)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-05 16:23:35 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 143ec07556 [Bot] Update dependencies (#2374)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-05-05 16:23:25 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 048ff03577 chore(deps): bump the actions group with 2 updates (#2379)
Bumps the actions group with 2 updates: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) and [wntrblm/nox](https://github.com/wntrblm/nox).


Updates `actions/attest-build-provenance` from 2.2.3 to 2.3.0
- [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/c074443f1aee8d4aeeae555aebba3282517141b2...db473fddc028af60658334401dc6fa3ffd8669fd)

Updates `wntrblm/nox` from 2025.02.09 to 2025.05.01
- [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/2025.02.09...2025.05.01)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: wntrblm/nox
  dependency-version: 2025.05.01
  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>
2025-05-05 16:21:01 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> e91560e8a1 [pre-commit.ci] pre-commit autoupdate (#2376)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.6 → v0.11.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.6...v0.11.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-04-28 18:25:42 -04:00
Tim Felgentreffpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
020a91baa8 feat: support GraalPy (#1538)
* Properly close files used for testing

* Add support for GraalPy

* Help GraalPy discover build tools on Windows

* Expect manylinux-interpreters ensure graalpy* warning in pip

* Workaround GraalPy bugs on Windows

* Workaround oracle/graalpython#491 also when uv is not available

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

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

* Update azure-pipelines.yml

* Update azure-pipelines.yml

* refacotor: use pathlib.write_text

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

* Include GraalPy in docker_warmup and remove workaround for installing it late

---------

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>
2025-04-28 18:25:17 -04:00
Matthieu DarboisandJoe Rickerby 5f8d06ff55 feat: initial support for riscv64 (#2366)
* feat: initial support for riscv64

* use enable for riscv64

* Apply review suggestion

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

* update documentation

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-04-28 12:05:03 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0463d26066 chore(deps): bump astral-sh/setup-uv from 5 to 6 in the actions group (#2375)
Bumps the actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).


Updates `astral-sh/setup-uv` from 5 to 6
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v5...v6)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '6'
  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>
2025-04-28 12:02:52 -04:00
Shantanu 4449d7b5fa chore: type check install_certifi.py (#2365) 2025-04-28 12:02:43 -04:00
Joe Rickerby e9a8781000 Merge branch '2.x' 2025-04-26 11:46:15 +01: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
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 4e5b8ee8e8 [Bot] Update dependencies (#2368)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-04-23 09:36:36 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 03f1905bb9 [pre-commit.ci] pre-commit autoupdate (#2370)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.5 → v0.11.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.5...v0.11.6)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-04-21 22:45:23 +01:00
Henry Schreinerandmayeut 99183d68ac ci: add Windows ARM (#2362)
* ci: add Windows ARM

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

* ci: install rust on Windows ARM

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

* ci: different action for rust

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

* ci: no sync required (avoid dev group)

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

* fix: expected wheels on Windows ARM64

* fix: skip python 3.8 on Windows ARM64

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-04-15 14:49:17 -04:00
Matthieu Darbois 95e3a6575f fix(CI): Cirrus CI macos jobs (#2355)
* fix(CI): Cirrus CI macos_arm64_cp38 job

* fix(CI): run on macos-runner:sequoia

* doc: update Cirrus CI minimal example
2025-04-14 17:20:26 -04:00
Matthieu Darbois e9fae06abe chore: rework virtual environments creation for tests (#2356)
As mentioned in https://github.com/pypa/cibuildwheel/pull/2347#discussion_r2029821701

Rework the way test venvs are created to reuse the same mechanisms as build venvs.
2025-04-14 17:20:08 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 1fcca39f6d [Bot] Update dependencies (#2359)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-04-14 17:13:04 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 625240d4ff [pre-commit.ci] pre-commit autoupdate (#2360)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.4 → v0.11.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.4...v0.11.5)
- [github.com/python-jsonschema/check-jsonschema: 0.32.1 → 0.33.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.32.1...0.33.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-04-14 17:11:26 -04:00
Joe RickerbyandHenry Schreiner 1d34f85c63 General docs improvements for v3.0 (#2280)
* Split the docs into sections

* Split the options page into two pages - so the reference is more clean

* Reorder/Tidy up this 'deliver' page to make auto-deploy more obvious

* Fix up some links

* Merge the platform docs into a single page to clean up the navbar.

* Split the setup page to make a page dedicated to CI setup

* Move general platform-specific information into the platforms page

* Tidy up the platforms page

* Remove some redundancy in the cpp/platforms docs

* Clarify this section

* Move another FAQ entry to the platforms page

* Remove some out-of-date entries from the FAQ

* A few corrections to the options page

* Fix some broken links

* Improve the Docker references

* Update bump version filename

* Improve options TOC information density

* whitespace

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

* Convert the normal test invocation to `pytest ./tests`, not placeholder

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-04-13 08:16:55 +02:00
Dimitri Papadopoulos Orfanos 74a7d8a68e chore: add a selection of PERF rules (#2313) 2025-04-13 01:06:33 -04:00
Henry Schreiner 93314a8d65 refactor: pull out dependency_constraint (#2347)
* refactor: pull out dependency_constraint

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

* refactor: address feedback

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

* ci: better parallel

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-04-09 09:58:57 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 8e2cc78913 [pre-commit.ci] pre-commit autoupdate (#2352)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.2 → v0.11.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.2...v0.11.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-04-08 06:31:59 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 08cbc6f8d0 chore(deps): bump actions/create-github-app-token from 1 to 2 in the actions group (#2351)
chore(deps): bump actions/create-github-app-token in the actions group

Bumps the actions group with 1 update: [actions/create-github-app-token](https://github.com/actions/create-github-app-token).


Updates `actions/create-github-app-token` from 1 to 2
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](https://github.com/actions/create-github-app-token/compare/v1...v2)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: '2'
  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>
2025-04-08 06:31:44 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 1172caba2b [Bot] Update dependencies (#2350)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-04-08 06:31:23 +02:00
Russell Keith-MageeandJoe Rickerby 830e79e11c feat: add the ability to declare safe tools in a cross-build environment. (#2317)
* Add the ability to declare safe tools in a cross-build environment.

* Add an xfail if cmake isn't available on the test machine.

* Placate linter regarding positional args.

* Rework test to provide more robust confirmation of safe tools.

* Remove a test skip condition that is no longer needed.

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

* Rename the setting to xbuild-tools.

* Add docs to clarify that xbuild-tools is transitive.

* Raise a warning if xbuild-tools isn't defined.

* Correct a bad copy-paste in the schema generator.

* .. and now fix the indentation.

* Move sentinel handling earlier into the parsing process.

* Remove serialization from tests that won't start a test suite.

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-04-07 08:20:20 +02:00
Henry SchreinerandMatthieu Darbois 2aaa489371 fix: support wider range of verbosity settings on other build backends (#2339)
* fix: support wider range of verbosity settings on other build backends

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

* Apply suggestions from code review

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

* refactor: address review comments

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>
2025-04-06 18:50:24 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 6c426a3a17 [Bot] Update dependencies (#2346)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-04-01 23:28:17 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 55bd91a92a [pre-commit.ci] pre-commit autoupdate (#2343)
updates:
- [github.com/python-jsonschema/check-jsonschema: 0.31.3 → 0.32.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.3...0.32.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-04-01 10:25:09 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 279221e0d6 [Bot] Update dependencies (#2341)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-03-31 12:46:45 -04:00
Matthieu Darbois 0dd98e95f1 ci: revert QEMU workaround (#2340) 2025-03-30 09:44:32 +01:00
Russell Keith-Magee 4ef7b3770f ci: correct Github Actions CI instability for iOS. (#2337)
* Correct Github Actions CI instability for iOS.

* Add a dummy serial test so CircleCI passes serial test discovery.
2025-03-28 10:33:51 -04:00
Joe Rickerby 094782a41d Merge branch '2.x' 2025-03-24 21:30:34 +00: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
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 2bb8b1e73d [pre-commit.ci] pre-commit autoupdate (#2333)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-24 20:58:34 +01:00
Matthieu Darbois e6eff86c98 feat: move default manylinux build to manylinux_2_28 (#2330) 2025-03-24 17:00:44 +00:00
Joe Rickerby 141e702872 feat: stricter selector parsing, refactor to platforms module (#2291)
* feat: stricter selector parsing, refactor to `platforms` module

- Use a different method to build nothing
- Make the check aware of enable groups

* optimise unit tests - a specific platform module API for all configs

Unit test time: 26.2s -> 13.1s

* Remove unnecessary get_platform_module function
2025-03-24 16:58:47 +00:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>mayeut
4db404ca50 [Bot] Update dependencies (#2332)
* Update dependencies

* fix: stop producing `cibuildwheel/resources/constraints-python37.txt`

---------

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-03-24 11:50:40 -04: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
Matthieu DarboisandJoe Rickerby 059d8ea25a feat: drop EOL manylinux images (#2316)
* drop EOL manylinux images

* chore: use multi-arch manylinux images as a source for image tag

Using multi-arch images directly messes with parallel tests for now.
Using them as a source for image tag ensures that all architecture specific tags are pointing to the same tag for a given manylinux / musllinux policy.

* Apply review suggestion

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

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-03-22 12:26:38 -04:00
Matthieu Darbois 3b9c8d4d22 fix: don't seed setuptools/wheel in tests virtual environments. (#2329) 2025-03-22 12:25:58 -04:00
Henry Schreiner 462c99589a fix: always resolve --python argument (#2328)
fix: always resolve the --python argument

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-03-22 07:32:25 +01:00
Hadi AlqattanandHenry Schreiner fccd32f9b2 Doc: add a note about Rustc's minimum MACOSX_DEPLOYMENT_TARGET requirement (#2326)
* doc: add a note about rustc's minimum MACOSX_DEPLOYMENT_TARGET requirement

* Update docs/platforms/macos.md

* Update faq.md

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
2025-03-22 07:29:45 +01:00
Henry Schreinerandmayeut be2a707315 feat: use build by default (#2321)
* feat: use build by default

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

* fix: test/test_before_build.py:test

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-03-21 09:49:04 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreinermayeut
21888b531f [pre-commit.ci] pre-commit autoupdate (#2320)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0)

* chore: use Path.iterdir

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

* fix: Path.iterdir usage

os.listdir returned entry names rather than their full path

---------

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>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-03-21 12:51:51 +00:00
Joe Rickerby 54a0cd0b22 Merge branch '2.x'
# Conflicts:
#	cibuildwheel/__init__.py
#	cibuildwheel/options.py
#	cibuildwheel/resources/pinned_docker_images.cfg
#	cibuildwheel/resources/virtualenv.toml
#	docs/setup.md
#	examples/github-deploy.yml
#	unit_test/options_test.py
2025-03-21 10:41:39 +00:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 1826f70682 [Bot] Update dependencies (#2319)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-03-17 10:10:58 -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
Matthieu Darbois 572f2a72b3 fix: image deprecation warning (#2314) 2025-03-12 08:41:51 +00:00
26e12290b5 feat: add support for building iOS wheels. (#2286)
* Add support for building iOS wheels.

* Replace use of system() in test binary module.

* Restored the 'minimal' approach of the minimal examples.

* Split out platform details into standalone pages, and expand iOS platform details.

* More doc corrections.

* Bump support package to include fix for python/cpython#130292

* Ensure iOS tests are all run on the same xdist worker.

* More iOS documentation tweaks.

* Factor out common xcode version test utility.

* Simplify iOS to a single platform with an expanded interpretation of arch.

* I guess I should update the iOS tests as well...

* Additional safety for missing iOS test output.

* Remove DYLD_LIBRARY_PATH from the iOS environment.

* Make test-sources mandatory for iOS builds.

* Updates and clarifications to documentation.

* Clarify what a slice is.

* Normalize use of underscores in platform name.

* Modify auto target to be matching CPU only.

* Use consistent ordering of platforms in examples.

* Use consistent naming in iOS archiectures.

* Placate the linter.

* Miscellaneous cleanups picked up by @joerick's review.

* Correct the list of expected wheels.

* Correct which 'native' we're actually checking.

* Correct the docs links so they're all relative.

* Correct the identification of free threaded builds.

* Use target instead of host to describe the platform we're building for.

* Rework iOS test to remove issue with log completeness.

* Convert errors to FatalError

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

* Removed a repeated check for a valid python.

* Update bin/update_pythons.py to update iOS support packages.

* Document that iOS CI is available on other platforms.

* Restore a comment needed for some platforms.

* Small cleanups identified in code review

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

* Simplify logic to appease linter.

* Modify dependency constraint handling to use new API.

* Cosmetic change to trigger a CI rebuild.

---------

Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-03-11 16:48:35 -04:00
Matthieu Darbois eefd48e266 chore: only build on push to main for Travis CI (#2295) 2025-03-11 16:44:25 -04:00
Henry SchreinerandJoe Rickerby 915d15cc32 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-11 16:44:06 -04:00
Joe Rickerbyandpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 25f2d3f313 Inline dependency-versions syntax (#2122)
* Write docs for inline dependency-versions

* Change the `inline` keyword to `packages` for better readability

* Implement inline package constraints

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

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

* Add schema for TOML properties

* Change the parsing of the option to parse filenames as-is

* Add a unit test for table-parsing of the option

* Remove unneeded shlex.quote on the dependency-version test

* Tidy-ups, comments, docs fixes

* Add test for empty packages option value

* Fix empty packages scenario

And, remove some optionals to reduce the problem space

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-10 21:45:54 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> f5e502aa88 [pre-commit.ci] pre-commit autoupdate (#2311)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10)
- [github.com/python-jsonschema/check-jsonschema: 0.31.2 → 0.31.3](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.2...0.31.3)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-10 16:30:50 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 849921caef chore(deps): bump actions/attest-build-provenance from 2.2.2 to 2.2.3 in the actions group (#2310)
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 2.2.2 to 2.2.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/bd77c077858b8d561b7a36cbe48ef4cc642ca39d...c074443f1aee8d4aeeae555aebba3282517141b2)

---
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>
2025-03-10 16:30:36 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 8c51c26bf7 [Bot] Update dependencies (#2309)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-03-10 16:30:18 -04:00
Joe Rickerby c136aefe20 chore: optimise unit tests (#2293)
* optimise unit tests - Cache loading the config TOML

Unit test time: 13.1s -> 10.5s

* optimise unit tests - use the same pyproject validator across the tests

Unit test time: 10.5s -> 5.7s

* optimise unit tests - selector optimisation

Unit test time: 5.7s -> 5.1s

* optimise unit tests - build options computation cache

Unit test time: 5.1s -> 3.2s

* Use functools.cache rather than functools.lru_cache

* Don't overwrite build_options when applying functools.cache
2025-03-10 21:29:07 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> bddd438a88 chore(deps): bump actions/attest-build-provenance from 2.2.0 to 2.2.2 in the actions group (#2299)
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 2.2.0 to 2.2.2
- [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/520d128f165991a6c774bcb264f323e3d70747f4...bd77c077858b8d561b7a36cbe48ef4cc642ca39d)

---
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>
2025-03-06 16:55:05 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> ee94e7fdf8 [Bot] Update dependencies (#2298)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-03-06 16:54:56 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ea26d56b5a [pre-commit.ci] pre-commit autoupdate (#2301)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.7 → v0.9.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.7...v0.9.9)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-06 16:54:30 -05:00
Joe Rickerby efa1bedd1f Merge branch '2.x' 2025-03-01 13:35:51 +00:00
Joe Rickerby 6cccd09a31 Bump version: v2.23.0 2025-03-01 13:27:50 +00:00
Matthieu Darbois 0b052a43b7 fix(ci): Cirrus CI does not allow armv7l builds on aarch64 (#2296) 2025-03-01 11:51:38 +00:00
Matthieu Darbois aa1534d2a3 chore(2.x): update dependencies (#2294) 2025-03-01 11:49:19 +00:00
Joe Rickerby 9e72cd9b54 feat: Print FatalError messages using Logger (#2292)
* feat: Print FatalError messages using Logger

This makes them appear GHA annotations in CI logs, which makes them easier to spot. I've also added the "cibuildwheel: " prefix to those errors, I think it helps when viewing a log to know which tool is talking to you - it can be difficult to understand with build tools.

* Nicer error message on invalid architecture

* Fix some message formatting and test expectations
2025-03-01 10:47:44 +01:00
Henry Schreiner 708cf1d32b style: remove from future import annotations
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Henry Schreiner 1b63074d76 chore: remove PopenBytes & TYPE_CHECKING
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Henry Schreiner 783be9bc2f chore: add pathlib check
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Henry Schreiner 3e37148923 chore: remove staticmethod to appease buggy pylint check
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Henry Schreiner 183a7c3f1d chore: some cleanup from ruff --preview checks
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Henry Schreiner 2f62696400 chore: use StrEnum from Python 3.11
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Henry Schreiner 63a7c44b7f chore: cleanup for 3.11+
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-28 16:38:15 -05:00
Lehman Garrison 88ef8dbd29 fix: avoid races in filesystem ops (#2287)
Use `exist_ok` and `missing_ok` in pathlib operations where we can.
2025-02-26 10:56:35 -05:00
Russell Keith-MageeandJoe Rickerby 4d37797aa6 Correct handling of test_sources on universal macOS builds. (#2284)
* Correct handling of test_sources on universal macOS builds.

* Make the test sources modifications macOS only.

* Don't use unittest discovery - that needs an actual test.

* Add comment explaining test source creation

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

* Simplify dict usage.

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-02-26 09:13:09 +00:00
Matthieu Darbois 7e5810c550 feat: drop python 3.6 & 3.7 support (#2282) 2025-02-25 14:23:01 -05: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
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> e061af0b82 [pre-commit.ci] pre-commit autoupdate (#2288)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.6 → v0.9.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.6...v0.9.7)
- [github.com/python-jsonschema/check-jsonschema: 0.31.1 → 0.31.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.1...0.31.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-02-25 07:44:56 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 6578d1a4f9 [Bot] Update dependencies (#2285)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-02-25 07:44:10 +01:00
Henry Schreiner b08f1ba966 chore: use nox 2025.2.9 features (#2274)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2025-02-25 07:43:09 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 34315ff651 [Bot] Update dependencies (#2278)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-02-19 15:01:11 -05:00
Matthieu Darbois c93d51ec54 feat: add armv7l in auto_archs when running on aarch64 (#2259)
* feat: add armv7l in auto_archs when running on aarch64

This depends on aarch32 EL0 support and thus is done conditionally.

* ci(travis): move to Ubuntu 22.04 / cp312
2025-02-12 10:54:44 -05:00
Matthieu Darbois c738289583 chore: use pypa/manylinux for manylinux_2_31_armv7l (#2269) 2025-02-12 10:52:48 -05:00
Matthieu Darbois a0f457675d doc: add a warning about x86-64-v2 for manylinux_2_34 (#2270) 2025-02-12 10:52:12 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> a787e5d7c9 [Bot] Update dependencies (#2272)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-02-12 10:51:47 -05:00
Matthieu Darbois 9cca4ce881 feature: add PyPy 3.11 (#2268) 2025-02-12 10:51:33 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 91b09c1c85 [pre-commit.ci] pre-commit autoupdate (#2275)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.4 → v0.9.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.4...v0.9.6)
- [github.com/pre-commit/mirrors-mypy: v1.14.1 → v1.15.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.1...v1.15.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-02-10 23:24:45 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fad71e8d6d chore(deps): bump wntrblm/nox from 2024.10.09 to 2025.02.09 in the actions group (#2273)
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.10.09 to 2025.02.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.10.09...2025.02.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>
2025-02-10 11:17:31 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 3805787fe7 [Bot] Update dependencies (#2267)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-02-09 10:54:24 +01:00
Maxim Evtush 9bfd0800fe Update LICENSE (#2264) 2025-02-06 10:41:03 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 3c5c62cf8c [pre-commit.ci] pre-commit autoupdate (#2261)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.3 → v0.9.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.3...v0.9.4)
- [github.com/codespell-project/codespell: v2.4.0 → v2.4.1](https://github.com/codespell-project/codespell/compare/v2.4.0...v2.4.1)
- [github.com/python-jsonschema/check-jsonschema: 0.31.0 → 0.31.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.31.0...0.31.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-02-05 21:54:56 -05:00
Joe Rickerby 0dcc90f3e4 fix: add missing schema for test-sources (#2258) 2025-02-03 15:10:44 -05:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 36259f4582 [Bot] Update dependencies (#2260)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-02-03 15:09:37 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b656a829c8 chore(deps): bump actions/attest-build-provenance from 2.1.0 to 2.2.0 in the actions group (#2254)
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 2.1.0 to 2.2.0
- [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/7668571508540a607bdfd90a87a560489fe372eb...520d128f165991a6c774bcb264f323e3d70747f4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-29 16:09:01 -05:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
e43288dd77 [pre-commit.ci] pre-commit autoupdate (#2255)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.2 → v0.9.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.2...v0.9.3)
- [github.com/codespell-project/codespell: v2.3.0 → v2.4.0](https://github.com/codespell-project/codespell/compare/v2.3.0...v2.4.0)

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

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

* 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>
2025-01-28 11:18:32 -05:00
Matthieu Darbois b605f8fec8 chore: refactor cibuildwheel.utils (#2252)
* chore: refactor cibuildwheel.utils

* rework build_frontend extra flags

* ci(fix): use tonistiigi/binfmt:qemu-v8.1.5 image for qemu
2025-01-27 14:35:56 -05:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>mayeut
318a963cc3 [Bot] Update dependencies (#2253)
* Update dependencies

* ci(fix): use tonistiigi/binfmt:qemu-v8.1.5 image for qemu

* fix(test): implement retry for test_container_removed

The test is flaky on some platforms. Implement retry rather than just skip.

---------

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2025-01-27 14:35:31 -05:00
Matthieu Darbois 3f123af8a1 feat: Add support for ubuntu-24.04-arm GHA runner (#2135)
* ci(gha): add tests on ubuntu-24.04-arm

* address review comments
2025-01-21 14:49:59 -05:00
Matthieu DarboisandJoe Rickerby d134edc652 chore: drop deprecated options related to CIBW_ENABLE (#2095)
* chore: drop deprecated options

* Add --enable option

* Fix pyodide tests


---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-01-21 12:53:01 -05:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 730b64249f [pre-commit.ci] pre-commit autoupdate (#2136)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.9.1 → v0.9.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.1...v0.9.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-01-21 07:48:07 +01:00
Matthieu Darbois 526df2f696 chore: fix deprecations warnings in update-dependencies workflow (#2134) 2025-01-21 07:47:53 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 9c2ab266a6 [Bot] Update dependencies (#2123)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-01-20 11:17:58 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> c65b088000 [pre-commit.ci] pre-commit autoupdate (#2127)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.9.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.9.1)
- [github.com/python-jsonschema/check-jsonschema: 0.30.0 → 0.31.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.30.0...0.31.0)

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-01-20 11:17:01 +00:00
Matthieu DarboisandJoe Rickerby a96545b729 feat: use python3.11+ for cibuildwheel driver (#1912)
* chore: use SPEC 0 schedule for cibuildwheel

* remove support for {python} and {pip} in commands

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

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-01-16 10:45:18 -05:00
hidewrong 1608f7567e chore: remove redundant word (#2128) 2025-01-14 22:18:19 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 25e87d8558 [pre-commit.ci] pre-commit autoupdate (#2124)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.8.4 → v0.8.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.4...v0.8.6)
- [github.com/pre-commit/mirrors-mypy: v1.14.0 → v1.14.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.0...v1.14.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-01-10 15:37:43 +00:00
Trim21andJoe Rickerby 66295e07b9 download cpython installer to cache dir on macos (#2108)
* cache cpython install on macos

* Update cibuildwheel/macos.py

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

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-01-10 15:28:35 +00:00
Agriya Khetarpal 6a3c9552c5 Bump to Pyodide 0.27 (#2117)
* Bump to Pyodide 0.27

* Update the Pyodide target's constraints
2025-01-10 15:28:09 +00:00
Russell Keith-MageeandJoe Rickerby 0b82613d6f Add a test-sources configuration option. (#2062)
* Add test_sources configuration option.

* Add pyodide implementation.

* Modify default test behavior to run in the project directory.

* Update the test for running in the project directory.

* Apply suggestions from code review

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

* Add shlex quoting to test-sources.

* Restructure ctypes example to avoid issues running from the project folder.

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
2025-01-10 15:24:24 +00:00
Matthieu Darbois 4d0d9f6fd0 ci: warm-up docker images (#2099)
* ci: warm-up docker images

* don't cache when running emulation tests

* skip graalpy cache

* add comment about the single worker case

* enforce docker warm-up succeeds in CI

* skip warm-up on architectures where it does not do any good
2025-01-10 15:24:02 +00:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 34dbe0e952 chore(deps): bump the actions group across 1 directory with 2 updates (#2116)
Bumps the actions group with 2 updates in the / directory: [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) and [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).


Updates `actions/attest-build-provenance` from 1.4.4 to 2.1.0
- [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/ef244123eb79f2f7a7e75d99086184180e6d0018...7668571508540a607bdfd90a87a560489fe372eb)

Updates `astral-sh/setup-uv` from 4 to 5
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: astral-sh/setup-uv
  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>
2025-01-05 14:21:42 +00:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 9358823214 [Bot] Update dependencies (#2105)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2025-01-04 13:30:21 +00:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 148b87f6fe [pre-commit.ci] pre-commit autoupdate (#2107)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.8.0 → v0.8.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.0...v0.8.4)
- [github.com/pre-commit/mirrors-mypy: v1.13.0 → v1.14.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.13.0...v1.14.0)
- [github.com/python-jsonschema/check-jsonschema: 0.29.4 → 0.30.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.29.4...0.30.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-12-24 12:20:22 +01:00
Matthieu Darbois 9c75ea15c2 feat: add manylinux_2_34 (#2098) 2024-11-28 07:52:46 +01:00
Matthieu Darbois 7e66fea502 ci/doc: move azure macOS build to macOS-13 (#2101)
The macOS-12 environment is deprecated.
2024-11-28 07:52:14 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 38f0901d38 chore(deps): bump astral-sh/setup-uv from 3 to 4 in the actions group (#2100)
Bumps the actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).


Updates `astral-sh/setup-uv` from 3 to 4
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v3...v4)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  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-11-25 21:12:12 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 6df11f0f28 [Bot] Update dependencies (#2096)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-11-25 21:10:24 +01:00
Henry Schreiner 3a0db72c17 docs: fix typo in enable (#2097) 2024-11-25 08:24:51 +01:00
Matthieu Darbois 116bf7490d docs: use non deprecated option for free-threading (#2094) 2024-11-23 21:41:05 -05:00
Matthieu Darbois 648448b46a fix: cibuildwheel.schema.json (#2093) 2024-11-23 21:40:46 -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
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> b1ed7bb3bc [Bot] Update dependencies (#1993)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-09-03 17:16:41 -04:00
Joe RickerbyandMatthieu Darbois ca08f5fbcf Customise merge behaviour depending on option. Fix #1803 (#1984)
Fix for #1803, where the method used for merging the config-settings option - string concatenation - was of little use.

I broke out the TableFmt object into a protocol-like object called OptionFormat, where the formatting and merge rules can be customised more deeply. This is more explicit, and lets us be a bit more precise with how TOML object get converted into the options.

I intend to use the flexibility of the OptionsFormat structure to tackle the remaining abiguity around environment variable quoting, as seen in the xfails in the options_test.py unit test and discussed in #1271.


Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2024-09-03 16:05:00 +01:00
pre-commit-ci[bot] 229029e112 [pre-commit.ci] pre-commit autoupdate (#1994) 2024-09-03 07:43:40 +01:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 3294577c17 [pre-commit.ci] pre-commit autoupdate (#1987)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.6.1 → v0.6.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.1...v0.6.2)
- [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2)
- [github.com/python-jsonschema/check-jsonschema: 0.29.1 → 0.29.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.29.1...0.29.2)

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-27 12:21:14 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 8f51efbc3e [Bot] Update dependencies (#1985)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-08-26 10:41:21 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ebdbb3cbd5 chore(deps): bump actions/attest-build-provenance from 1.4.1 to 1.4.2 in the actions group (#1986)
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.1 to 1.4.2
- [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/310b0a4a3b0b78ef57ecda988ee04b132db73ef8...6149ea5740be74af77f260b9db67e633f6b0a9a1)

---
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-08-26 10:40:32 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
3a30bec9ef [pre-commit.ci] pre-commit autoupdate (#1978)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.7 → v0.6.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.7...v0.6.1)

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

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

* chore: ruff python-version not needed

---------

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-08-21 00:34:31 -04:00
Matthieu Darbois 126b81916d chore(ci): podman is now pre-installed on GHA runners (#1974)
podman is now pre-installed on GHA runners
2024-08-19 11:28:24 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 9abea5b73b [Bot] Update dependencies (#1976)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-08-19 11:26:53 -04:00
Matthieu Darbois 170e7aaeb9 fix: TravisCI failures & status (#1970)
* fix: TravisCI failures & status
* Allow failure on s390x
2024-08-15 17:48:24 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 5b570a2451 [Bot] Update dependencies (#1963)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-08-14 16:00:33 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> ffd7dddcc7 [pre-commit.ci] pre-commit autoupdate (#1967)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.5.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.5.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-14 16:00:02 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b0b13d3a16 chore(deps): bump actions/attest-build-provenance from 1.4.0 to 1.4.1 in the actions group (#1964)
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.0 to 1.4.1
- [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/210c1913531870065f03ce1f9440dd87bc0938cd...310b0a4a3b0b78ef57ecda988ee04b132db73ef8)

---
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-08-14 15:59:47 -04:00
Matthieu Darbois b1781e4606 fix: AppVeyor CI tests & sample (#1971) 2024-08-14 15:57:17 -04:00
Henry Schreinerandmayeut 3580374b18 fix: support empty CIBW_PLATFORM (#1969)
* fix: support empty CIBW_PLATFORM

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

* fix: support empty CIBW_PLATFORM

* Add test

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2024-08-14 15:56:06 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> c6dd39bd83 [Bot] Update dependencies (#1954)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-08-07 16:08:57 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> dd8fe86308 [pre-commit.ci] pre-commit autoupdate (#1958)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.5 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.5...v0.5.6)
- [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-07 16:07:39 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2e12f05d5e chore(deps): bump actions/attest-build-provenance from 1.3.3 to 1.4.0 in the actions group (#1956)
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.3.3 to 1.4.0
- [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/5e9cb68e95676991667494a6a4e59b8a2f13e1d0...210c1913531870065f03ce1f9440dd87bc0938cd)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-07 16:07:28 -04:00
Henry Schreiner bd033a4447 Bump version: v2.20.0 2024-08-03 22:37:32 -04:00
Ewout ter Hoevenpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>mayeut
9d023cf77a Build CPython 3.13 by default (#1950)
* Build CPython 3.13 by default

Enable building with CPython 3.13 by default, no longer requiring prerelease_pythons flag to be set true.

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

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

* README.md: Add note about free-threaded support

* build_selector_test.py: cp313t still behind flag

Even with 3.13 being enabled by default, cp313t is still behind a flag for now.

This commit updates the test accordingly.

* fix unit tests

---------

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-08-03 14:46:45 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 8a69dbd9d4 [Bot] Update dependencies (#1949)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-08-03 12:05:10 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 147de6f4f7 [pre-commit.ci] pre-commit autoupdate (#1947)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.4 → v0.5.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.4...v0.5.5)
- [github.com/python-jsonschema/check-jsonschema: 0.29.0 → 0.29.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.29.0...0.29.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-30 10:01:48 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> e217e44661 [Bot] Update dependencies (#1946)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-07-29 11:02:55 -04:00
Matt Richards 871ef6b506 tests: show xfail traceback summaries again (#1943)
Revert "tests: don't print xfails (#1865)"

This reverts commit 8d86d3122b.
2024-07-23 09:14:06 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
fac72f024a [pre-commit.ci] pre-commit autoupdate (#1944)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.2 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.2...v0.5.4)
- [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.0)

* chore: type ignore no longer needed

---------

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-07-23 09:26:50 +01:00
Conor MacBrideandMatthieu Darbois d4c332104d feat: add CIBW_ALLOW_EMPTY environment variable (#1937)
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2024-07-23 09:25:09 +01:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 5af5df4e4f [Bot] Update dependencies (#1935)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-07-21 19:56:36 +02:00
Conor MacBride 6c5cf87871 docs: fix nox docs command (#1938) 2024-07-21 11:37:43 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 46f4108f23 chore(deps): bump actions/attest-build-provenance from 1.3.2 to 1.3.3 in the actions group (#1936)
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.3.2 to 1.3.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/bdd51370e0416ac948727f861e03c2f05d32d78e...5e9cb68e95676991667494a6a4e59b8a2f13e1d0)

---
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-07-21 11:29:36 +02:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 1da439f8dc [pre-commit.ci] pre-commit autoupdate (#1939)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.1 → v0.5.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.1...v0.5.2)
- [github.com/python-jsonschema/check-jsonschema: 0.28.6 → 0.29.0](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.6...0.29.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-21 11:29:11 +02:00
Matthieu Darbois 7af9d039fb fix: do not use uv on Windows PyPy 3.8 (#1941)
fix: do not use uv on PyPy 3.8

uv 0.2.25+ does not work with PyPy 3.8
Given PyPy 3.8 is EOL, just disable uv for this config rather than ask for it to be supported again in uv.
2024-07-21 10:51:43 +02:00
MusicalNinjaDadJoe RickerbyHenry Schreinerpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
de84624d70 fix: set VSCMD_ARG_TGT_ARCH based on targeted architecture (#1876)
* set VSCMD_ARG_TGT_ARCH based on targetted architecture

* only set VSCMD_ARG_TGT_ARCH if not already set

* add unit tests to check setuptools correctly identifies windows arch

* only run tests on windows

* arm64 fails on azure pipelines (only)

* Update windows.py

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

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

* Apply suggestions from code review

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

* update test_env_set to validate FatalError is raised on env collision

* remove noqa ARG001 (no longer needed)

---------

Co-authored-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: Joe Rickerby <joerick@mac.com>
2024-07-12 14:32:42 -04:00
Matthieu Darbois cac121a67d chore: update examples to use python 3.12 (#1919) 2024-07-09 09:15:44 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 604da878cb [Bot] Update dependencies (#1923)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-07-09 09:14:32 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> eaf0051162 chore(deps): bump actions/attest-build-provenance from 1.1.2 to 1.3.2 in the actions group (#1925)
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.1.2 to 1.3.2
- [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/173725a1209d09b31f9d30a3890cf2757ebbff0d...bdd51370e0416ac948727f861e03c2f05d32d78e)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 09:14:08 -04:00
Mike Taves 8dcf71e02d DOC: fix link to correct PR number (#1928) 2024-07-09 09:13:20 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 92f0142a3e [pre-commit.ci] pre-commit autoupdate (#1927)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.0 → v0.5.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.0...v0.5.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-09 09:13:04 -04:00
Matthieu Darbois 6c8347f465 chore: use python from setup-python step to run nox (#1921) 2024-07-08 07:59:44 +02:00
Matthieu Darbois a4d24056ef chore: move AlreadyBuiltWheelError & NonPlatformWheelError to errors.py (#1920) 2024-07-07 09:47:03 +02:00
Henry Schreiner 7e5a838a63 Bump version: v2.19.2 2024-07-02 08:07:58 -04:00
Henry Schreiner 19e1b8a4b0 chore: attestations (#1916) 2024-07-02 07:55:40 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 73581ae579 [Bot] Update dependencies (#1917)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-07-02 07:55:10 -04:00
pre-commit-ci[bot]pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>Henry Schreiner
f21ff5e84c [pre-commit.ci] pre-commit autoupdate (#1914)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0)
- [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1)

* style follow suggestion

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-07-01 22:08:31 -04:00
Henry Schreiner 973946b172 fix: support --no-isolation with build[uv] (#1889)
* fix: support --no-isolation with build[uv]

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

* docs: mention caveat

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-07-01 20:15:09 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 5ea40e6ff3 [Bot] Update dependencies (#1913)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-07-01 10:58:15 -04:00
Matthieu Darbois 6a36f6494b feat: add macOS on GitLab (#1911) 2024-06-30 10:59:58 +02:00
Matthieu Darbois 31de15bcd9 chore: remove nosetests from tests and docs (#1898)
fix #499
2024-06-30 10:21:33 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 3179fd215a [Bot] Update dependencies (#1900)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-06-30 08:07:29 +02:00
Matthieu Darbois 184d4e1280 fix(ci): CircleCI / Azure Pipelines / GitLab (#1899)
* fix(ci): CircleCI

CircleCI does not support Intel macOS runners anymore.
c.f. https://discuss.circleci.com/t/macos-intel-support-deprecation-in-january-2024/48718

* fix(ci): Azure Pipelines

* fix(ci): GitLab
2024-06-29 14:12:13 -04:00
Eric LarsonandMatthieu Darbois 08165ffeed DOC: Document macOS 3.8 arm64 workaround (#1871)
* BUG: Fix bug with macOS 3.8 arm64 URL

* TST: Ping

* DOC: Update

* FIX: Remove old

* DOC: Both

* 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-06-27 11:52:32 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 9bb03835e5 [pre-commit.ci] pre-commit autoupdate (#1895)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.9 → v0.4.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.9...v0.4.10)
- [github.com/python-jsonschema/check-jsonschema: 0.28.5 → 0.28.6](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.5...0.28.6)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-26 16:16:34 -04:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 2552303ff3 [Bot] Update dependencies (#1894)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-06-25 07:54:08 +02:00
Matthieu Darbois 5b74582a72 chore: remove pip beta workaround (#1892) 2024-06-23 14:16:44 +02:00
Matthieu Darbois 4470eb0dd9 chore: remove pip beta workaround (#1891) 2024-06-22 14:38:46 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 8bda260d3a [Bot] Update dependencies (#1885)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-06-22 10:57:56 +02:00
Lukas Burgholzer 5b5a95bdc2 docs: properly escape extra in build[uv] (#1886) 2024-06-19 08:01:35 +02:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 561fa32683 [pre-commit.ci] pre-commit autoupdate (#1888)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.8 → v0.4.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.8...v0.4.9)
- [github.com/python-jsonschema/check-jsonschema: 0.28.4 → 0.28.5](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.4...0.28.5)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-18 15:17:45 -04:00
Matthieu Darbois 70913f0a49 chore: remove cached_property from utils (#1884) 2024-06-17 23:01:08 +02:00
Siddhant Goel f49397f151 docs: add streaming-form-data to projects.yml (#1883) 2024-06-17 09:43:38 -04:00
Matthieu Darbois c1b8a12cd5 chore(tests): run a project with tests using build[uv] frontend (#1880) 2024-06-17 01:07:35 -04:00
Henry Schreiner 932529cab1 Bump version: v2.19.1 2024-06-13 02:38:27 -04:00
Henry Schreiner 70fb1c4a1a fix: specify full path to non-venv Python (#1881)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-13 08:18:09 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 405a475ac7 [Bot] Update dependencies (#1879)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-06-12 23:16:49 -04:00
Nathan Goldbaum 78da7bafcc docs: update README.md (#1874) 2024-06-12 11:32:27 -04:00
Henry Schreiner 8d86d3122b tests: don't print xfails (#1865)
tests: skip warnings on xfails for now

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-12 10:46:24 -04:00
Eric Larson 89a5cfe272 DOC: Fix bug with link (#1870) 2024-06-11 21:24:44 +02:00
Henry Schreiner 8d5d84e0fc fix: accept current Python version if acceptable for Pyodide (#1868)
* fix: accept current Python version if acceptable for Pyodide

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-11 08:48:42 +02:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 4ada77dea4 [pre-commit.ci] pre-commit autoupdate (#1869)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.4.8)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-11 00:32:52 -04:00
Henry Schreiner 829441f4a0 fix: warning in generate schema script (#1866)
* fix: warning in generate schema script

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

* docs: add minor update to update process

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

* Update docs/contributing.md

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-11 00:32:38 -04:00
Henry Schreiner a8d190a111 Bump version: v2.19.0 2024-06-10 11:28:29 -04:00
bf817c6dc8 refactor: error handling to use exceptions (#1719)
* Refactor error handling to use exceptions

cibuildwheel has up until now handled most errors by printing an error message to sys.stderr and calling sys.exit. Others were handled using Logger.error, depending on the context. We also had return codes, but these weren't explicitly defined anywhere.

This makes that convention more explicit and codified. Now to halt the program, the correct thing to do is to throw a cibuildwheel.errors.FatalError exception - that is caught in main() and printed before exiting. The existing behaviour was kept - if an error occurs within a build step (probably something to do with the build itself), the Logger.error() method is used. Outside of a build step (e.g. a misconfiguration), the behaviour is still to print 'cibuildwheel: <message>'

I also took the opportunity to add a debugging option `--debug-traceback` (and `CIBW_DEBUG_TRACEBACK`), which you can enable to see a full traceback on errors.

(I've deactivated the flake8-errmsg lint rule, as it was throwing loads of errors and these error messages aren't generally seen in a traceback context)

* add noqa rule

* Apply suggestions from code review

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

* Return to flake8-errmsg conformance

* Code review suggestions

* Subclass Exception rather than SystemExit

* apply error handling to new code and fix merge issues

* Apply review suggestion

* fix: merge issue

* Update cibuildwheel/errors.py

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: mayeut <mayeut@users.noreply.github.com>
2024-06-10 10:36:20 -04:00
Henry Schreiner 384c8d5c82 feat: build[uv] (#1856) 2024-06-09 15:45:31 -04:00
Matthieu Darbois c37e5a2d13 fix: util.move_file shall not use log.notice (#1862)
Using  `log.notice` result in many unwanted annotations, at least in GitHub Actions.
If we want to print some information about a specific file being moved, it shall be done by the caller - or adding an option to the function - and use `print`.
2024-06-09 19:03:12 +02:00
cibuildwheel-bot[bot]cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>Henry SchreinerMatthieu Darbois
130fdd2548 feat: free-threaded Python for macOS, MACOSX_DEPLOYMENT_TARGET updates (#1854)
* Update dependencies

* feat: macos free-threading

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

* fix: set min target to 10.13 for 3.13 and floor it always

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

* fix: tests and corrected pypy mins

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

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>
2024-06-08 10:15:04 +02:00
Henry Schreiner ed120540d9 chore: bump pyodide (#1859)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-06-08 01:38:06 -04:00
MusicalNinjaDadHenry Schreinerpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
6c6e0f6ba5 fix: handle case where output_dir does not already exist on macos & windows (#1851)
* replace `with suppress(FileNotFoundError)` by `.unlink(missing_ok=True)` for macos

* also use `.unlink(missing_ok=True)` in pyodide and windows for consistency

* remove contextlib imports which are no longer required

* Apply suggestions from code review

* explicity resolve and create output location

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

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

* use explicit str for move

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

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

* update comments based on review feedback

* Apply suggestions from code review

* Break out functionality to move files to util.py

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

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

* raise instance of IsADirectoryError with meaningful message

* Don't need a comment and a exception message

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-07 11:37:20 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 877d3bf649 [pre-commit.ci] pre-commit autoupdate (#1852)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.5 → v0.4.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.5...v0.4.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-04 08:34:28 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 21e9ef1800 [Bot] Update dependencies (#1848)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-06-04 08:32:55 +02:00
Henry Schreiner c333b131d3 fix: include VIRTUAL_ENV variable (#1842)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-05-29 08:20:02 +02:00
Henry Schreiner 4c2ed1e632 chore: remove pip <21.3 workaround (#1841)
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
2024-05-29 07:57:06 +02:00
Hood ChathamMatthieu DarboisHenry Schreinerpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
9f2a3cb67c feat: add Pyodide support (#1456)
* feat: add Pyodide support

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Hood Chatham <roberthoodchatham@gmail.com>
Co-authored-by: Matthieu Darbois <mayeut@users.noreply.github.com>

tests: fix two merge issues

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

fix: include schema

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

* Try to fix xbuildenv path

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

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

Try to install pyodide-build from main branch

Try again

Try again

Update constraints file

Try again

Remove unused variable

Drop constraints

Remove --download option

Fix xbuildenv install

Try again

Try again

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

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

* fix: remove pinning on pyodide

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

* Update for Pyodide 0.26.0a5

* Install pyodide-build from pypi

* Update docs/options.md

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

* Unxfail things that look like they were just a version mismatch

* refactor: add constraints for pyodide

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

* chore: minor cleanup

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

* Apply suggestions from code review

* Apply suggestion from code review

* refactor: minor touchup

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

* ci: xfail the pyodide test

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

* review: use a pinned version of node

* fix tests

* review: error out on Windows

* test: check node & test on macos arm64

* chore: minor cleanup

* Add reference to emscripten libc issue

* Apply suggestion from code review

* review: use a pinned pip in test virtual environment

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

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

* chore: rework test virtual environment seed packages

* chore: workaround direct invocation of pytest

This allows to still test direct invocation of `pytest` on most platforms (including pyodide on Linux) but falls back to `python -m pytest` when running pyodide on macOS.

* Use release version of pyodide

* fix: tests for 0.26.0 & parallel initialization of xbuildenv

* Debug CI

* fix: test/test_build_frontend_args.py

* Revert "Debug CI"

This reverts commit 917646cffc96dbfc619c47d1c03a828f447bcdb7.

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
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-05-28 08:31:36 -04:00
pre-commit-ci[bot]andpre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> 687406f664 [pre-commit.ci] pre-commit autoupdate (#1840)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.4 → v0.4.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.4...v0.4.5)
- [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0)
- [github.com/python-jsonschema/check-jsonschema: 0.28.3 → 0.28.4](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.3...0.28.4)

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-05-27 22:34:22 -04:00
Matthieu Darbois 2e6b11e10c chore(ci): split emulation tests per architecture (#1839) 2024-05-27 22:34:10 -04:00
Matthieu Darbois b713086763 chore: better determinism for the test virtual environment (#1838)
* chore: better determinism for the test virtual environment

* review: apply suggestions
2024-05-27 23:04:25 +02:00
cibuildwheel-bot[bot]andcibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com> 8f574d107b [Bot] Update dependencies (#1836)
Update dependencies

Co-authored-by: cibuildwheel-bot[bot] <83877280+cibuildwheel-bot[bot]@users.noreply.github.com>
2024-05-27 12:28:13 -04:00
Matthieu Darbois 97da90432e chore: rework single python tests (#1835)
* chore: rework single python tests

* chore(tests): limit number of builds in test_abi3

* chore(tests): limit number of builds in test_before_all

* apply review suggestion

use `os.path.samefile` rather than comparing `os.stat` directly.
2024-05-26 12:41:20 +02:00
Matthieu Darbois 67ee9dda92 chore(tests): do not use hard-coded paths in tests (#1834)
* chore(tests): do not use hard-coded paths in tests

* review suggestion

use `os.path.samefile` rather than comparing `os.stat` directly.
2024-05-26 12:40:11 +02:00
200 changed files with 17736 additions and 6494 deletions
+37 -31
View File
@@ -1,25 +1,43 @@
version: 2
version: 2.1
commands:
cibw_prepare_environment:
description: "Prepare the environment for testing."
steps:
- run:
name: Prepare the environment.
command: bash .circleci/prepare.sh
cibw_run_tests:
description: "Runs tests, with CIBW_ENABLE=all on the main branch"
steps:
- run:
name: Test
command: |
if [ "${CIRCLE_BRANCH}" == "main" ]; then
echo "INFO: Exporting CIBW_ENABLE=all for main branch test run."
export CIBW_ENABLE=all
else
echo "INFO: CIBW_ENABLE not set for this branch test run."
fi
venv/bin/python ./bin/run_tests.py
no_output_timeout: 30m
jobs:
osx-python3.9:
osx-python312:
macos:
xcode: 12.5.1
xcode: 15.4.0
resource_class: macos.m1.medium.gen1
environment:
PYTHON: python3
steps:
- checkout
- cibw_prepare_environment
- cibw_run_tests
- run:
name: Prepare the environment.
command: bash .circleci/prepare.sh
- run:
name: Test.
command: venv/bin/python ./bin/run_tests.py
no_output_timeout: 30m
linux-python3.9:
linux-python312:
docker:
- image: circleci/python:3.9
- image: cimg/python:3.12
environment:
PYTHON: python3
# Temporarily restrict the tests that are run on CircleCI to prevent
@@ -28,14 +46,8 @@ jobs:
steps:
- checkout
- setup_remote_docker
- run:
name: Prepare the environment.
command: bash .circleci/prepare.sh
- run:
name: Test.
command: venv/bin/python ./bin/run_tests.py
no_output_timeout: 30m
- cibw_prepare_environment
- cibw_run_tests
linux-aarch64:
machine:
@@ -48,19 +60,13 @@ jobs:
PYTEST_ADDOPTS: -k "unit_test or main_tests or test_0_basic or test_docker_images"
steps:
- checkout
- run:
name: Prepare the environment.
command: bash .circleci/prepare.sh
- run:
name: Test.
command: venv/bin/python ./bin/run_tests.py
no_output_timeout: 30m
- cibw_prepare_environment
- cibw_run_tests
workflows:
version: 2
all-tests:
jobs:
- osx-python3.9
- linux-python3.9
- osx-python312
- linux-python312
- linux-aarch64
+8 -4
View File
@@ -2,11 +2,15 @@
set -o errexit
set -o xtrace
if [ "$(uname -s)" == "Darwin" ]; then
sudo softwareupdate --install-rosetta --agree-to-license
else
docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
fi
$PYTHON --version
$PYTHON -m pip --version
$PYTHON -m virtualenv --version
$PYTHON -m virtualenv --no-setuptools --no-wheel -p "$PYTHON" venv
$PYTHON -m venv venv
venv/bin/python -m pip install -U pip
venv/bin/python -m pip install -e ".[dev]"
venv/bin/python -m pip install -e. --group test
venv/bin/python -m pip freeze
venv/bin/python --version
+37 -20
View File
@@ -1,12 +1,12 @@
only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesIncludeOnly('docs/*', '.pre-commit-config.yaml')) || $CIRRUS_BRANCH =~ 'cirrus.*'
only_if: changesInclude('.cirrus.yml') || ($CIRRUS_BRANCH == "main" && !changesIncludeOnly('.github/*', 'bin/*', 'docs/*', '.circleci/*', '.travis.yml', '.pre-commit-config.yaml', '.readthedocs.yml', 'azure-pipelines.yml', 'README.md', 'mkdocs.yml', 'noxfile.py')) || $CIRRUS_BRANCH =~ 'cirrus.*'
run_tests: &RUN_TESTS
install_cibuildwheel_script:
- python -m pip install -e ".[dev]" pytest-custom-exit-code
- python -m pip install -U pip
- python -m pip install -e. --group test
run_cibuildwheel_tests_script:
- python ./bin/run_tests.py
linux_x86_task:
timeout_in: 120m
compute_engine_instance:
@@ -15,9 +15,15 @@ linux_x86_task:
platform: linux
cpu: 8
memory: 8G
env:
VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH}
install_pre_requirements_script:
- apt install -y python3-venv python-is-python3
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- add-apt-repository -y ppa:deadsnakes/ppa
- apt-get update
- apt-get install -y python3.12-venv
- python3.12 -m venv ${VENV_ROOT}
<<: *RUN_TESTS
linux_aarch64_task:
@@ -28,14 +34,20 @@ linux_aarch64_task:
platform: linux
cpu: 4
memory: 4G
env:
VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH}
install_pre_requirements_script:
- apt install -y python3-venv python-is-python3
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
- add-apt-repository -y ppa:deadsnakes/ppa
- apt-get update
- apt-get install -y python3.12-venv
- python3.12 -m venv ${VENV_ROOT}
<<: *RUN_TESTS
windows_x86_task:
# The task takes ~55 minutes while the timeout happens
# after 60 minutes by default, let's allow some wiggle room.
# The task takes ~55 minutes while the timeout happens after 60 minutes by default,
# let's allow some wiggle room.
timeout_in: 120m
windows_container:
image: cirrusci/windowsservercore:visualstudio2022
@@ -43,30 +55,35 @@ windows_x86_task:
memory: 8G
install_pre_requirements_script:
- choco install -y --no-progress python3 --version 3.10.6
- certutil -generateSSTFromWU roots.sst
- certutil -addstore -f root roots.sst
- del roots.sst
- choco install -y --no-progress python3 --version 3.12.4
- refreshenv
- echo PATH=%PATH% >> "%CIRRUS_ENV%"
- powershell -Command "$cleanPath = $env:PATH -replace ';+$', ''; Add-Content -Path $env:CIRRUS_ENV -Value ('PATH=' + $cleanPath)"
<<: *RUN_TESTS
macos_arm64_task:
macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode
image: ghcr.io/cirruslabs/macos-runner:sequoia
env:
PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH
VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH}
install_pre_requirements_script:
- brew install python@3.10
- brew install python@3.12
- python3.12 -m venv ${VENV_ROOT}
<<: *RUN_TESTS
macos_arm64_cp38_task:
macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode
image: ghcr.io/cirruslabs/macos-runner:sequoia
env:
PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH
PYTEST_ADDOPTS: --run-cp38-universal2 -k 'test_cp38_arm64_testing_universal2_installer or test_arch_auto'
VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH}
PYTEST_ADDOPTS: --run-cp38-universal2 -k 'test_cp38_arm64_testing_universal2_installer or test_arch_auto or test_dummy_serial'
install_pre_requirements_script:
- brew install python@3.10
- brew install python@3.12
- python3.12 -m venv ${VENV_ROOT}
- curl -fsSLO https://www.python.org/ftp/python/3.8.10/python-3.8.10-macos11.pkg
- sudo installer -pkg python-3.8.10-macos11.pkg -target /
- rm python-3.8.10-macos11.pkg
+7
View File
@@ -0,0 +1,7 @@
# Specification files for platform maintainers
cibuildwheel/platforms/ios.py @freakboy3742
cibuildwheel/platforms/pyodide.py @hoodmane @ryanking13 @agriyakhetarpal
cibuildwheel/platforms/android.py @mhsmith
# Constraints files
cibuildwheel/resources/constraints-pyodide*.txt @hoodmane @ryanking13 @agriyakhetarpal
+2
View File
@@ -9,3 +9,5 @@ updates:
actions:
patterns:
- "*"
cooldown:
default-days: 7
+19 -15
View File
@@ -7,36 +7,40 @@ on:
types:
- published
permissions: {}
jobs:
dist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build SDist and wheel
run: pipx run build
- uses: actions/upload-artifact@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
name: cibw-sdist
path: dist/*
persist-credentials: false
- name: Check metadata
run: pipx run twine check dist/*
- uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0
publish:
needs: [dist]
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
environment: pypi
environment:
name: pypi
url: https://pypi.org/p/cibuildwheel
permissions:
id-token: write
attestations: write
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: cibw-*
name: Packages
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
- name: Generate artifact attestation for sdist and wheel
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: "dist/cibuildwheel-*"
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
attestations: true
+273 -54
View File
@@ -4,76 +4,180 @@ on:
push:
branches:
- main
- 2.x
pull_request:
types:
- opened
- synchronize
- reopened
- labeled
paths-ignore:
- 'docs/**'
- .ci*
- bin/*
- docs/**
- examples/azure-pipelines-*
- examples/ci*
- examples/travis-ci-*
- .pre-commit-config.yaml
- .readthedocs.yml
- .travis.yml
- README.md
- azure-pipelines.yml
- mkdocs.yml
- noxfile.py
workflow_dispatch:
# allow manual runs on branches without a PR
permissions: {}
concurrency:
group: test-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Linters (mypy, flake8, etc.)
name: Linters (mypy, ruff, etc.)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
id: python
with:
python-version: "3.x"
- uses: pre-commit/action@v3.0.1
- name: Check manifest
run: pipx run nox -s check_manifest
- uses: j178/prek-action@79f765515bd648eb4d6bb1b17277b7cb22cb6468 # v2.0.0
- name: PyLint checks
run: pipx run nox -s pylint -- --output-format=github
run: pipx run --python "${STEPS_PYTHON_OUTPUTS_PYTHON_PATH}" nox -s pylint -- --output-format=github
env:
STEPS_PYTHON_OUTPUTS_PYTHON_PATH: ${{ steps.python.outputs.python-path }}
sample:
name: Generate a sample project
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
name: Install Python
with:
python-version: "3.x"
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install dependencies
run: uv sync --no-dev --group test
- name: Generate a sample project
run: uv run --no-sync -m test.test_projects test.test_0_basic.basic_project sample_proj
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: sample_proj
path: sample_proj
test:
name: Test cibuildwheel on ${{ matrix.os }} (${{ matrix.python_version }})
needs: lint
name: Test on ${{ matrix.os }} (${{ matrix.python_version }}) ${{ matrix.test_select }}
needs: sample
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-13, macos-14]
python_version: ['3.12']
include:
# Min Python
- os: ubuntu-latest
python_version: '3.8'
python_version: '3.11'
# Max Python
- os: ubuntu-latest
python_version: '3.14'
- os: ubuntu-latest
python_version: '3.13'
test_select: android
- os: ubuntu-24.04-arm
python_version: '3.13'
- os: windows-latest
python_version: '3.13'
- os: windows-11-arm
python_version: '3.13'
- os: macos-15-intel
python_version: '3.13'
- os: macos-15
python_version: '3.13'
- os: macos-15-intel
python_version: '3.13'
test_select: ios
- os: macos-15
python_version: '3.13'
test_select: ios
# Exercise iOS on a non-default simulator.
test_runtime: 'args: --simulator "iPhone 16e,OS=18.5"'
- os: macos-15-intel
python_version: '3.13'
test_select: android
# Exercise Android on a non-default simulator
test_runtime: 'args: --managed minVersion'
- os: macos-15
python_version: '3.13'
test_select: android
timeout-minutes: 180
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
name: Install Python ${{ matrix.python_version }}
with:
python-version: ${{ matrix.python_version }}
allow-prereleases: true
# Install podman on this CI instance for podman tests on linux
# Snippet from: https://github.com/redhat-actions/podman-login/blob/main/.github/workflows/example.yml
- name: Install latest podman
if: runner.os == 'Linux'
run: |
. /etc/os-release
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -sSfL "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key" | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install podman
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
# free some space to prevent reaching GHA disk space limits
- name: Clean docker images
if: runner.os == 'Linux'
- name: Free up disk space
if: runner.os == 'Linux' && matrix.test_select != 'android'
run: |
docker system prune -a -f
df -h
sudo rm -rf $ANDROID_HOME/ndk /opt/hostedtoolcache/CodeQL \
/usr/local/lib/node_modules /usr/local/share/chromium \
/usr/local/share/powershell
df -m
# for oci_container unit tests
- name: Set up QEMU
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Set up Xcode
if: ${{ startsWith(matrix.os, 'macos-15') }}
run: |
# GitHub recommends explicitly selecting the desired Xcode version
sudo xcode-select --switch /Applications/Xcode_16.4.app
- name: Install dependencies
run: |
python -m pip install ".[test]"
run: uv sync --no-dev --group test
- name: Generate a sample project
- uses: joerick/pr-labels-action@0543b277721e852d821c6738d449f2f4dea03d5f # v1.0.9
- name: Set CIBW_ENABLE
shell: bash
run: |
python -m test.test_projects test.test_0_basic.basic_project sample_proj
if [[ "$GITHUB_REF_NAME" == "main" ]]; then
CIBW_ENABLE=all
else
# get the default CIBW_ENABLE value from the test module
CIBW_ENABLE=$(uv run --no-sync python -c 'import sys, test.utils as c; sys.stdout.write(c.DEFAULT_CIBW_ENABLE)')
# if this is a PR, check for labels
if [[ -n "$GITHUB_PR_LABEL_CI_PYPY" ]]; then
CIBW_ENABLE+=" pypy"
fi
if [[ -n "$GITHUB_PR_LABEL_CI_GRAALPY" ]]; then
CIBW_ENABLE+=" graalpy"
fi
fi
echo "CIBW_ENABLE=${CIBW_ENABLE}" >> $GITHUB_ENV
- name: Download a sample project
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sample_proj
path: sample_proj
- name: Run a sample build (GitHub Action)
uses: ./
@@ -82,69 +186,184 @@ jobs:
output-dir: wheelhouse
env:
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
CIBW_BUILD_FRONTEND: ${{ matrix.test_select && 'build' || 'build[uv]' }}
CIBW_PLATFORM: ${{ matrix.test_select }}
CIBW_TEST_RUNTIME: ${{ matrix.test_runtime }}
- name: Run a sample build (GitHub Action, only)
uses: ./
if: matrix.test_select == ''
with:
package-dir: sample_proj
output-dir: wheelhouse_only
only: cp312-${{ runner.os == 'Linux' && 'manylinux_x86_64' || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }}
only: cp313-${{ runner.os == 'Linux' && (runner.arch == 'ARM64' && 'manylinux_aarch64' || 'manylinux_x86_64') || (runner.os == 'Windows' && 'win_amd64' || 'macosx_x86_64') }}
- name: Create custom configuration file
shell: bash
run: |
cat > sample_proj/cibw.toml <<EOF
[tool.cibuildwheel]
# Only build on CPython 3.12 on native arch
# Only build on CPython 3.13 on native arch
archs = ["native"]
build = "cp312-*"
build = "cp313-*"
# Skip musllinux
skip = "*-musllinux*"
EOF
- name: Run a sample build (GitHub Action, config-file)
uses: ./
env:
CIBW_PLATFORM: ${{ matrix.test_select }}
CIBW_TEST_RUNTIME: ${{ matrix.test_runtime }}
with:
package-dir: sample_proj
output-dir: wheelhouse_config_file
config-file: sample_proj/cibw.toml
- name: Check Action artefacts
- name: Check Action artifacts
shell: bash
run: |
test $(find wheelhouse -name '*.whl' | wc -l) -ge 1
test $(find wheelhouse_only -name '*.whl' | wc -l) -eq 1
test $(find wheelhouse_config_file -name '*.whl' | wc -l) -eq 1
- uses: actions/upload-artifact@v4
- name: Check Action artifacts (native build only)
if: matrix.test_select == ''
shell: bash
run: |
test $(find wheelhouse_only -name '*.whl' | wc -l) -eq 1
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: wheelhouse/*.whl
- name: Test cibuildwheel
run: |
python ./bin/run_tests.py --run-podman
env:
CIBW_TEST_RUNTIME: ${{ matrix.test_runtime }}
run: uv run --no-sync bin/run_tests.py --test-select=${{ matrix.test_select || 'native' }} ${{ (runner.os == 'Linux' && runner.arch == 'X64') && '--run-podman' || '' }}
test-emulated:
name: Test emulated cibuildwheel using qemu
emulated-archs:
name: Get qemu emulated architectures
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 180
outputs:
archs: ${{ steps.archs.outputs.archs }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.x"
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install dependencies
run: uv sync --no-dev --group test
- name: Get qemu emulated architectures
id: archs
run: |
python -m pip install ".[test]"
OUTPUT=$(uv run --no-sync python -c "from json import dumps; from test.utils import EMULATED_ARCHS; print(dumps(EMULATED_ARCHS))")
echo "${OUTPUT}"
echo "archs=${OUTPUT}" >> "$GITHUB_OUTPUT"
test-emulated:
name: Test Linux ${{ matrix.arch }} using qemu
needs: emulated-archs
runs-on: ubuntu-latest
timeout-minutes: 180
strategy:
matrix:
arch: ${{ fromJSON(needs.emulated-archs.outputs.archs) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.x"
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install dependencies
run: uv sync --no-dev --group test
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v3
with:
platforms: all
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Run the emulation tests
run: |
pytest --run-emulation test/test_emulation.py
env:
MATRIX_ARCH: ${{ matrix.arch }}
run: uv run --no-sync pytest --run-emulation "$MATRIX_ARCH" test/test_emulation.py
test-pyodide:
name: Test pyodide
needs: sample
runs-on: ubuntu-24.04
timeout-minutes: 180
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
name: Install Python 3.12
with:
python-version: '3.12'
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install dependencies
run: uv sync --no-dev --group test
- name: Download a sample project
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sample_proj
path: sample_proj
- name: Run a sample build (GitHub Action)
uses: ./
with:
package-dir: sample_proj
output-dir: wheelhouse
env:
CIBW_PLATFORM: pyodide
- name: Run tests with 'CIBW_PLATFORM' set to 'pyodide'
run: uv run --no-sync ./bin/run_tests.py
env:
CIBW_PLATFORM: pyodide
test-uv-extras:
name: Test uv extra on ${{ matrix.os }} ${{ matrix.test_select }}
needs: sample
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
test_select: android
- os: windows-latest
- os: windows-11-arm
- os: macos-15-intel
- os: macos-15
- os: macos-15-intel
test_select: android
- os: macos-15
test_select: android
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Download a sample project
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sample_proj
path: sample_proj
- name: Run a sample build (GitHub Action)
uses: ./
with:
extras: uv
package-dir: sample_proj
output-dir: wheelhouse
env:
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
CIBW_BUILD: cp314-*
CIBW_BUILD_FRONTEND: 'build[uv]'
CIBW_PLATFORM: ${{ matrix.test_select }}
+24 -15
View File
@@ -14,17 +14,32 @@ on:
schedule:
- cron: '0 6 * * 1' # "At 06:00 on Monday."
permissions: {}
jobs:
update-dependencies:
name: Update dependencies
if: github.repository_owner == 'pypa' || github.event_name != 'schedule'
runs-on: ubuntu-latest
permissions:
contents: write
environment: ${{ github.ref == 'refs/heads/main' && github.repository == 'pypa/cibuildwheel' && 'update-dependencies-workflow' || '' }}
steps:
- uses: actions/checkout@v4
- uses: wntrblm/nox@2024.04.15
# we use this step to grab a Github App auth token, so that PRs generated by this workflow
# run the GHA tests.
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: generate-token
if: github.ref == 'refs/heads/main' && github.repository == 'pypa/cibuildwheel'
with:
python-versions: "3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13"
app-id: ${{ secrets.CIBUILDWHEEL_BOT_APP_ID }}
private-key: ${{ secrets.CIBUILDWHEEL_BOT_APP_PRIVATE_KEY }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: wntrblm/nox@5a277b752f6094150e25237d47c34168e2b7526e # 2026.02.09
- name: "Run update: dependencies"
run: nox --force-color -s update_constraints
@@ -33,18 +48,9 @@ jobs:
- 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
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
commit-message: Update dependencies
title: '[Bot] Update dependencies'
@@ -53,7 +59,10 @@ jobs:
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>"
sign-commits: true
token: ${{ steps.generate-token.outputs.token }}
delete-branch: true
labels: |
CI: GraalPy
CI: PyPy
dependencies
+4 -2
View File
@@ -21,10 +21,12 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Update the ${{ env.TAG_NAME }} tag
id: update-major-minor-tag
uses: joerick/update-vX.Y-tag-action@v1.0
uses: joerick/update-vX.Y-tag-action@c4cefb60c33b82e4aa73a469e4acf30ee48d5812 # v1.0.2
with:
source-tag: ${{ env.TAG_NAME }}
+24
View File
@@ -0,0 +1,24 @@
name: zizmor - GitHub Actions Security Analysis
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
permissions: {}
jobs:
zizmor:
name: Run zizmor 🌈
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
+8 -6
View File
@@ -84,9 +84,7 @@ celerybeat-schedule
# virtualenv
.venv
venv/
venv3/
venv2/
venv*/
ENV/
env/
env2/
@@ -112,8 +110,12 @@ all_known_setup.yaml
# mkdocs
site/
# Virtual environments
venv*
# PyCharm
.idea/
# Lockfiles
*.lock
*.pylock
# OS files
.DS_Store
+31 -5
View File
@@ -1,5 +1,5 @@
linux:
image: python:3.8
image: python:3.12
services:
- name: docker:dind
entrypoint: ["env", "-u", "DOCKER_HOST"]
@@ -13,9 +13,15 @@ linux:
# skip all but the basic tests
# (comment the below line in a PR to debug a Gitlab-specific issue)
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
CIBW_ENABLE: "all"
script:
- 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 -U pip
- python -m pip install -e. pytest-custom-exit-code --group test
- python ./bin/run_tests.py
windows:
@@ -23,9 +29,29 @@ windows:
variables:
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
before_script:
- choco install python -y --version 3.8.6
- choco install python -y --allow-downgrade --version 3.12.4
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
CIBW_ENABLE: "all"
script:
- py -m pip install -e ".[dev]" pytest-custom-exit-code
- py -m pip install -U pip
- py -m pip install -e. pytest-custom-exit-code --group test
- py bin\run_tests.py
tags:
- windows
- saas-windows-medium-amd64
macos:
image: macos-14-xcode-15
variables:
PYTEST_ADDOPTS: -k "unit_test or test_0_basic" --suppress-no-test-exit-code
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
CIBW_ENABLE: "all"
script:
- python3 -m pip install -U pip
- python3 -m pip install -e. pytest-custom-exit-code --group test
- python3 ./bin/run_tests.py
tags:
- saas-macos-medium-m1
+33 -24
View File
@@ -1,7 +1,9 @@
repos:
ci:
autoupdate_schedule: monthly
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: check-case-conflict
- id: check-merge-conflict
@@ -14,41 +16,47 @@ repos:
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
rev: 4924b0e01e032fea073ad04a1c5cfa7e4add0afb # frozen: v0.15.6
hooks:
- id: ruff
args: ["--fix", "--show-fixes"]
- id: ruff-check
args: ["--fix"]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
rev: a66e98df7b4aeeb3724184b332785976d062b92e # frozen: v1.19.1
hooks:
- id: mypy
name: mypy 3.8 on cibuildwheel/
exclude: ^cibuildwheel/resources/.*py|bin/generate_schema.py$
args: ["--python-version=3.8"]
name: mypy 3.11 on cibuildwheel/
args: ["--python-version=3.11"]
exclude: ^cibuildwheel/resources/_cross_venv.py$ # Requires Python 3.13 or later
additional_dependencies: &mypy-dependencies
- bracex
- nox
- build
- dependency-groups>=1.2
- humanize
- nox>=2025.2.9
- orjson
- packaging
- pyelftools
- pygithub
- pytest
- rich
- tomli
- tomli_w
- types-certifi
- types-click
- types-jinja2
- types-pyyaml
- types-requests
- types-setuptools
- uv
- validate-pyproject
- id: mypy
name: mypy 3.12
exclude: ^cibuildwheel/resources/.*py$
args: ["--python-version=3.12"]
name: mypy 3.14
args: ["--python-version=3.14"]
additional_dependencies: *mypy-dependencies
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
rev: 745eface02aef23e168a8afb6b5737818efbea95 # frozen: v0.11.0.1
hooks:
- id: shellcheck
@@ -58,25 +66,26 @@ repos:
name: Disallow improper capitalization
language: pygrep
entry: PyBind|Numpy|Cmake|Github|PyTest
types:
- markdown
types: [markdown]
exclude: ^docs/working-examples\.md$ # Autogenerated
- id: update-readme-changelog
name: Update README changelog
- id: cog
name: Cog the README
language: python
entry: bin/update_readme_changelog.py
files: ^docs/changelog.md$
pass_filenames: false
entry: cog -c -P -r -I ./bin README.md
files: '^(README\.md|docs/changelog\.md|docs/options\.md|bin/readme.*)$'
additional_dependencies: [cogapp>=3.5]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
hooks:
- id: codespell
args: ["-L", "sur", "-w"]
args: ["-w"]
exclude: ^docs/working-examples\.md$ # Autogenerated
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.3
rev: 9f48a48aa91a6040d749ad68ec70907d907a5a7f # frozen: 0.37.0
hooks:
- id: check-dependabot
- id: check-github-actions
+2 -6
View File
@@ -3,13 +3,9 @@
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.12"
os: ubuntu-24.04
commands:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
- uv venv
- uv pip install -e.[docs]
- NO_COLOR=1 .venv/bin/mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
- NO_COLOR=1 uv run --python 3.14 --managed-python --no-dev --group docs mkdocs build --strict --site-dir $READTHEDOCS_OUTPUT/html
-60
View File
@@ -1,60 +0,0 @@
os: linux
dist: focal
language: python
branches:
only:
- main
jobs:
include:
- name: Linux | x86_64 + i686 | Python 3.9
python: 3.9
services: docker
env: PYTHON=python
- name: Linux | arm64 | Python 3.9
python: 3.9
services: docker
arch: arm64-graviton2
group: edge
virt: vm
env: PYTHON=python
- name: Linux | ppc64le | Python 3.9
python: 3.9
services: docker
arch: ppc64le
allow_failure: True
env:
- PYTHON=python
# skip test_manylinuxXXXX_only, it uses too much disk space
# c.f. https://travis-ci.community/t/running-out-of-disk-space-quota-when-using-docker-on-ppc64le/11634
- PYTEST_ADDOPTS='-k "not test_manylinuxXXXX_only"'
- name: Windows | x86_64 | Python 3.9
os: windows
language: shell
before_install:
- choco upgrade python3 -y --version 3.9.13 --limit-output --params "/InstallDir:C:\\Python39"
env:
- PYTHON=C:\\Python39\\python
- name: Linux | s390x | Python 3.9
python: 3.9
services: docker
arch: s390x
env: PYTHON=python
install:
- $PYTHON -m pip install -U pip
- $PYTHON -m pip install -e ".[dev]" pytest-custom-exit-code
script: |
# travis_wait disable the output while waiting
# use the same kind of tricks as in multibuild
(while true; do echo "travis_keep_alive"; sleep 300; done) &
SPINNER_PID=$!
disown
$PYTHON ./bin/run_tests.py --num-processes 2
kill -9 ${SPINNER_PID}
+5 -7
View File
@@ -1,11 +1,9 @@
This is a summary of the host Python versions and platforms covered by the different CI platforms:
| | 3.8 | 3.9 | 3.10 | 3.12 |
|---------|--------------------------------------------------------|-----------------------|-----------|----------------|
| Linux | AppVeyor¹ / Azure Pipelines / GitLab¹ / GitHub Actions | CircleCI¹ / Travis CI | Cirrus CI | GitHub Actions |
| macOS | AppVeyor¹ / Azure Pipelines | CircleCI¹ / Travis CI | Cirrus CI | GitHub Actions |
| Windows | AppVeyor¹ / Azure Pipelines / GitLab¹ | Travis CI | Cirrus CI | GitHub Actions |
| | 3.11 | 3.12 | 3.13 | 3.14 |
|---------|----------------------------------|---------------------------------|----------------|----------------|
| Linux | Azure Pipelines / GitHub Actions | CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions | GitHub Actions |
| macOS | Azure Pipelines | CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions | |
| Windows | Azure Pipelines | Cirrus CI / GitLab¹ | GitHub Actions | |
> ¹ Runs a reduced set of tests to reduce CI load
Non-x86 architectures are covered on Travis CI using Python 3.9.
+5 -20
View File
@@ -1,24 +1,9 @@
This project is licensed under the 'BSD 2-clause license'.
Copyright 2017, Joe Rickerby and contributors. All rights reserved.
Copyright (c) 2017-2023, Joe Rickerby and contributors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+143 -118
View File
@@ -4,8 +4,6 @@ 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)
[![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)
[![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)
[![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)
@@ -16,33 +14,38 @@ cibuildwheel
Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platforms.
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platforms.
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 |
|----------------|----|-----|-----|-----|-----|----|-----|----|-----|-----|
| CPython 3.6 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.7 | ✅ | N/A | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| PyPy 3.7 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 |
| PyPy 3.9 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
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:
<sup>¹ PyPy is only supported for manylinux wheels.</sup><br>
| | 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 | Android | iOS | Pyodide |
|--------------------|----|-----|----|-----|-----|----|-----|----|-----|-----|---|-----|-----|-----|
| CPython 3.8 | ✅ | ✅ | ✅ | ✅ | N/A | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | N/A |
| CPython 3.12 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | N/A | N/A | ✅⁴ |
| CPython 3.13³ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | ✅ | ✅⁴ |
| CPython 3.14 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ | ✅⁵ | ✅ | ✅ | N/A |
| PyPy 3.8 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | 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 | N/A | N/A |
| PyPy 3.10 v7.3 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | 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 | N/A | N/A |
| GraalPy 3.11 v24.2 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | N/A | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
| GraalPy 3.12 v25.0 | ✅ | ✅ | ✅ | N/A | N/A | ✅¹ | N/A | ✅¹ | N/A | N/A | N/A | N/A | N/A | N/A |
<sup>¹ PyPy & GraalPy are only supported for manylinux wheels.</sup><br>
<sup>² Windows arm64 support is experimental.</sup><br>
<sup>³ CPython 3.13 is available using the [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.pypa.io/en/stable/options/#prerelease-pythons) option.</sup><br>
<sup>³ Free-threaded mode requires opt-in on 3.13 using [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable).</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
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
- Builds manylinux, musllinux, macOS, and Windows wheels for CPython, PyPy, and GraalPy
- Works on GitHub Actions, Azure Pipelines, 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)
- Runs your library's tests against the wheel-installed version of your library
@@ -53,18 +56,18 @@ Usage
`cibuildwheel` runs inside a CI service. Supported platforms depend on which service you're using:
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM |
|-----------------|-------|-------|---------|-----------|-----------|-------------|
| GitHub Actions | ✅ | ✅ | ✅ | ✅¹ | ✅ | ✅² |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² |
| Travis CI | ✅ | | ✅ | ✅ | | |
| AppVeyor | ✅ | ✅ | ✅ | | | ✅² |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | |
| Gitlab CI | ✅ | | ✅ | ✅¹ | | |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | |
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | Android | iOS |
|-----------------|-------|-------|---------|-----------|-----------|-------------|---------|-----|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅⁴ | ✅³ |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅⁴ | ✅³ |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | | ✅⁴ | ✅³ |
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | | ✅ | ✅³ |
| Cirrus CI | ✅ | ✅ | | ✅ | ✅ | | ✅⁴ | |
<sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br>
<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup>
<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture. </sup><br>
<sup>⁴ Building for Android requires the runner to be Linux x86_64, macOS ARM64 or macOS x86_64. Testing has [additional requirements](https://cibuildwheel.pypa.io/en/stable/platforms/#android).</sup><br>
<!--intro-end-->
@@ -73,6 +76,7 @@ Example setup
To build manylinux, musllinux, macOS, and Windows wheels on GitHub Actions, you could use this `.github/workflows/wheels.yml`:
<!--generic-github-start-->
```yaml
name: Build
@@ -84,28 +88,32 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-13, macos-14]
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
persist-credentials: false
# Used to host cibuildwheel
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.18.1
run: python -m pip install cibuildwheel==3.4.1
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
# to supply options, put them in 'env', like:
# env:
# CIBW_SOME_OPTION: value
# ...
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
```
<!--generic-github-end-->
For more information, including PyPI deployment, and the use of other CI services or the dedicated GitHub Action, check out the [documentation](https://cibuildwheel.pypa.io) and the [examples](https://github.com/pypa/cibuildwheel/tree/main/examples).
@@ -118,33 +126,53 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
<sup>Explore an interactive version of this diagram [in the docs](https://cibuildwheel.pypa.io/en/stable/#how-it-works).</sup>
Options
-------
> [!WARNING]
> Building and testing wheels executes arbitrary code from your project and its dependencies. Although cibuildwheel uses OCI containers and Pyodide for some builds, these provide no security guarantees - the code you're building and testing has full access to the environment that's invoking cibuildwheel.
>
> If you cannot trust all the code that's pulled in, maintain good security hygiene: keep the job that builds distributions separate from the job that uploads them to PyPI, handle secrets and credentials with care and rotate them regularly, and follow the principle of least privilege when granting permissions. Do not store sensitive data on CI runners.
<!--[[[cog from readme_options_table import get_table; print(get_table()) ]]]-->
<!-- This table is auto-generated from docs/options.md by bin/readme_options_table.py -->
| | Option | Description |
|---|--------|-------------|
| **Build selection** | [`CIBW_PLATFORM`](https://cibuildwheel.pypa.io/en/stable/options/#platform) | Override the auto-detected target platform |
| | [`CIBW_BUILD`](https://cibuildwheel.pypa.io/en/stable/options/#build-skip) <br> [`CIBW_SKIP`](https://cibuildwheel.pypa.io/en/stable/options/#build-skip) | Choose the Python versions to build |
| | [`CIBW_ARCHS`](https://cibuildwheel.pypa.io/en/stable/options/#archs) | Change the architectures built on your machine by default. |
| | [`CIBW_PROJECT_REQUIRES_PYTHON`](https://cibuildwheel.pypa.io/en/stable/options/#requires-python) | Manually set the Python compatibility of your project |
| | [`CIBW_PRERELEASE_PYTHONS`](https://cibuildwheel.pypa.io/en/stable/options/#prerelease-pythons) | Enable building with pre-release versions of Python if available |
| **Build customization** | [`CIBW_BUILD_FRONTEND`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) | Set the tool to use to build, either "pip" (default for now) or "build" |
| | [`CIBW_ENVIRONMENT`](https://cibuildwheel.pypa.io/en/stable/options/#environment) | Set environment variables needed during the build |
| | [`CIBW_ENVIRONMENT_PASS_LINUX`](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass) | Set environment variables on the host to pass-through to the container during the build. |
| | [`CIBW_BEFORE_ALL`](https://cibuildwheel.pypa.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
| | [`CIBW_BEFORE_BUILD`](https://cibuildwheel.pypa.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
| | [`CIBW_REPAIR_WHEEL_COMMAND`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each built wheel |
| | [`CIBW_MANYLINUX_*_IMAGE`<br/>`CIBW_MUSLLINUX_*_IMAGE`](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) | Specify alternative manylinux / musllinux Docker images |
| | [`CIBW_CONTAINER_ENGINE`](https://cibuildwheel.pypa.io/en/stable/options/#container-engine) | Specify which container engine to use when building Linux wheels |
| | [`CIBW_DEPENDENCY_VERSIONS`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) | Specify how cibuildwheel controls the versions of the tools it uses |
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
| | [`CIBW_BEFORE_TEST`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
| | [`CIBW_TEST_REQUIRES`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
| | [`CIBW_TEST_EXTRAS`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using extras_require |
| | [`CIBW_TEST_SKIP`](https://cibuildwheel.pypa.io/en/stable/options/#test-skip) | Skip running tests on some builds |
| **Other** | [`CIBW_BUILD_VERBOSITY`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of pip wheel |
|---|---|---|
| **Build selection** | [`platform`](https://cibuildwheel.pypa.io/en/stable/options/#platform) | Override the auto-detected target platform |
| | [`build`<br>`skip`](https://cibuildwheel.pypa.io/en/stable/options/#build-skip) | Choose the Python versions to build |
| | [`archs`](https://cibuildwheel.pypa.io/en/stable/options/#archs) | Change the architectures built on your machine by default. |
| | [`project-requires-python`](https://cibuildwheel.pypa.io/en/stable/options/#requires-python) | Manually set the Python compatibility of your project |
| | [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) | Enable building with extra categories of selectors present. |
| | [`allow-empty`](https://cibuildwheel.pypa.io/en/stable/options/#allow-empty) | Suppress the error code if no wheels match the specified build identifiers |
| **Build customization** | [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) | Set the tool to use to build, either "build" (default), "build\[uv\]", or "pip" |
| | [`config-settings`](https://cibuildwheel.pypa.io/en/stable/options/#config-settings) | Specify config-settings for the build backend. |
| | [`environment`](https://cibuildwheel.pypa.io/en/stable/options/#environment) | Set environment variables |
| | [`environment-pass`](https://cibuildwheel.pypa.io/en/stable/options/#environment-pass) | Set environment variables on the host to pass-through to the container. |
| | [`before-all`](https://cibuildwheel.pypa.io/en/stable/options/#before-all) | Execute a shell command on the build system before any wheels are built. |
| | [`before-build`](https://cibuildwheel.pypa.io/en/stable/options/#before-build) | Execute a shell command preparing each wheel's build |
| | [`xbuild-tools`](https://cibuildwheel.pypa.io/en/stable/options/#xbuild-tools) | Binaries on the path that should be included in an isolated cross-build environment. |
| | [`repair-wheel-command`](https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command) | Execute a shell command to repair each built wheel |
| | [`manylinux-*-image`<br>`musllinux-*-image`](https://cibuildwheel.pypa.io/en/stable/options/#linux-image) | Specify manylinux / musllinux container images |
| | [`container-engine`](https://cibuildwheel.pypa.io/en/stable/options/#container-engine) | Specify the container engine to use when building Linux wheels |
| | [`dependency-versions`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) | Control the versions of the tools cibuildwheel uses |
| | [`pyodide-version`](https://cibuildwheel.pypa.io/en/stable/options/#pyodide-version) | Specify the Pyodide version to use for `pyodide` platform builds |
| **Testing** | [`test-command`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | The command to test each built wheel |
| | [`before-test`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
| | [`test-sources`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Paths that are copied into the working directory of the tests |
| | [`test-requires`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
| | [`test-extras`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using `extras_require` |
| | [`test-groups`](https://cibuildwheel.pypa.io/en/stable/options/#test-groups) | Specify test dependencies from your project's `dependency-groups` |
| | [`test-skip`](https://cibuildwheel.pypa.io/en/stable/options/#test-skip) | Skip running tests on some builds |
| | [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) | Set environment variables for the test environment |
| | [`test-runtime`](https://cibuildwheel.pypa.io/en/stable/options/#test-runtime) | Controls how the tests will be executed. |
| **Debugging** | [`debug-keep-container`](https://cibuildwheel.pypa.io/en/stable/options/#debug-keep-container) | Keep the container after running for debugging. |
| | [`debug-traceback`](https://cibuildwheel.pypa.io/en/stable/options/#debug-traceback) | Print full traceback when errors occur. |
| | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
These options can be specified in a pyproject.toml file, as well; see [configuration](https://cibuildwheel.pypa.io/en/stable/options/#configuration).
<!--[[[end]]] (sum: dbfwOkj/k/) -->
These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
Working examples
----------------
@@ -158,28 +186,27 @@ Here are some repos that use cibuildwheel.
| Name | CI | OS | Notes |
|-----------------------------------|----|----|:------|
| [scikit-learn][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The machine learning library. A complex but clean config using many of cibuildwheel's features to build a large project with Cython and C++ extensions. |
| [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an analytical in-process SQL database management system |
| [pytorch-fairseq][] | ![github icon][] | ![apple icon][] ![linux icon][] | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. |
| [NumPy][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
| [Tornado][] | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
| [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. |
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
| [Tornado][] | ![github icon][] | ![linux icon][] ![apple icon][] ![windows icon][] | Tornado is a Python web framework and asynchronous networking library. Uses stable ABI for a small C extension. |
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
| [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an in-process SQL OLAP Database Management System |
| [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. |
| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
[scikit-learn]: https://github.com/scikit-learn/scikit-learn
[duckdb]: https://github.com/duckdb/duckdb
[pytorch-fairseq]: https://github.com/facebookresearch/fairseq
[NumPy]: https://github.com/numpy/numpy
[Tornado]: https://github.com/tornadoweb/tornado
[Matplotlib]: https://github.com/matplotlib/matplotlib
[NCNN]: https://github.com/Tencent/ncnn
[Prophet]: https://github.com/facebook/prophet
[Matplotlib]: https://github.com/matplotlib/matplotlib
[Tornado]: https://github.com/tornadoweb/tornado
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
[duckdb]: https://github.com/duckdb/duckdb
[Prophet]: https://github.com/facebook/prophet
[Kivy]: https://github.com/kivy/kivy
[appveyor icon]: docs/data/readme_icons/appveyor.svg
[github icon]: docs/data/readme_icons/github.svg
[azurepipelines icon]: docs/data/readme_icons/azurepipelines.svg
[circleci icon]: docs/data/readme_icons/circleci.svg
@@ -206,71 +233,58 @@ This is similar to static linking, so it might have some license implications. C
Changelog
=========
<!-- START bin/update_readme_changelog.py -->
<!-- [[[cog from readme_changelog import mini_changelog; print(mini_changelog()) ]]] -->
<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->
### v3.4.1
### v2.18.1
_2 April 2026_
- 🌟 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 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)
- ⚠️ Building for the experimental CPython 3.13 free-threading variant is now deprecated. That functionality will be removed in the next minor release. The [`enable`](https://cibuildwheel.pypa.io/en/stable/options/#enable) option `cpython-freethreading` is therefore also deprecated. Builds specifying `enable = "all"` no longer select `cpython-freethreading`. CPython 3.14 free-threading support remains available without the `enable` flag. (#2787)
- 🐛 iOS builds will no longer skip `repair-wheel-command` if it's defined in config (#2761)
- 🐛 Fix bug causing `uv` to fail when environments define PYTHON_VERSION or UV_PYTHON, conflicting with our venvs (#2795)
- ✨ cibuildwheel prints the selected build identifiers at the start of the build. (#2785)
- 🔐 The GitHub Action now references other actions with a full SHA (#2744)
### v3.4.0
### v2.18.0
_5 March 2026_
_12 May 2024_
- 🌟 You can now build wheels using `uv` as a build frontend. This should improve performance, especially if your project has lots of build dependencies. To use, set [`build-frontend`](https://cibuildwheel.pypa.io/en/stable/options/#build-frontend) to `uv`. (#2322)
- ⚠️ We no longer support running on Travis CI. It may continue working but we don't run tests there anymore so we can't be sure. (#2682)
- ✨ Improvements to building rust wheels on Android (#2650)
- 🐛 Fix bug with the GitHub Action on Windows, where PATH was getting unnecessarily changed, causing issues with meson builds. (#2723)
- ✨ Add support for quiet setting on `build` and `uv` from the cibuildwheel `build-verbosity` setting. (#2737)
- 📚 Docs updates, including guidance on using Meson on Windows (#2718)
- ✨ Adds CPython 3.13 support, under the prerelease flag [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.pypa.io/en/stable/options/#prerelease-pythons). This version of cibuildwheel uses 3.13.0b1. Free-threading mode is not available yet (#1657), waiting on official binaries (planned for beta 2) and pip support.
### v3.3.1
_While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.13 will be available in cibuildwheel without the flag._ (#1815)
_5 January 2026_
- ✨ Musllinux now defaults to `musllinux_1_2`. You can set the older `musllinux_1_1` via config if needed. (#1817)
- 🛠 No longer pre-seed setuptools/wheel in virtual environments (#1819)
- 🛠 Respect the constraints file when building with pip, matching build (#1818)
- 🛠 Use uv to compile our pinned dependencies, 10x faster and doesn't require special setup (#1778)
- 🐛 Fix an issue with the schema (#1788)
- 📚 Document the new delocate error checking macOS versions (#1766)
- 📚 Document Rust builds (#1816)
- 📚 Speed up our readthedocs builds with uv, 26 seconds -> 6 seconds to install dependencies (#1816)
- 🛠 Update dependencies and container pins, including updating to CPython 3.14.2. (#2708)
### v3.3.0
### v2.17.0
_12 November 2025_
_11 March 2024_
- 🐛 Fix an incompatibility with Docker v29 (#2660)
- ✨ Adds `test-runtime` option, to customise how tests on simulated/emulated environments are run (#2636)
- ✨ Adds support for new `manylinux_2_35` images on 32-bit ARM `armv7l`, offering better C++20 compatibility (#2656)
- ✨ `build[uv]` is now supported on Android (#2587)
- ✨ You can now install extras (such as `uv`) with a simple option on the GitHub Action (#2630)
- ✨ `{project}` and `{package}` placeholders are now supported in `repair-wheel-command` (#2589)
- 🛠 The versions set with `dependency-versions` no longer constrain packages specified by your `build-system.requires`. Previously, on platforms other than Linux, the constraints in this option would remain in the environment during the build. This has been tidied up make behaviour more consistent between platforms, and to prevent version conflicts. (#2583)
- 🛠 Improve the handling of `test-command` on Android, enabling more options to be passed (#2590)
- 📚 Docs improvements (#2618)
- 🌟 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).
- ✨ 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)
- 🛠 Improve how the GitHub Action passes arguments (#1757)
- 🛠 Remove a system-wide install of pipx in the GitHub Action (#1745)
- 🐛 No longer will cibuildwheel override the `PIP_CONSTRAINT` environment variable when using the `build` frontend. Instead it will be extended. (#1675)
- 🐛 Fix a bug where building and testing both `x86_86` and `arm64` wheels on the same runner caused the wrong architectures in the test environment (#1750)
- 🐛 Fix a bug that prevented testing a CPython 3.8 wheel targeting macOS 11+ on `x86_64` (#1768)
- 📚 Moved the docs onto the official PyPA domain - they're now available at https://cibuildwheel.pypa.io . (#1775)
- 📚 Docs and examples improvements (#1762, #1734)
### v3.2.1
_12 October 2025_
### v2.16.5
- 🛠 Update to CPython 3.14.0 final (#2614)
- 🐛 Fix the default MACOSX_DEPLOYMENT_TARGET on Python 3.14 (#2613)
- 📚 Docs improvements (#2617)
_30 January 2024_
- 🐛 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)
### v2.16.4
_28 January 2024_
- 🛠 Update manylinux pins to upgrade from a problematic PyPy version. (#1737)
<!-- END bin/update_readme_changelog.py -->
<!-- [[[end]]] (sum: NOLMd9iLDC) -->
---
@@ -290,12 +304,23 @@ Everyone interacting with the cibuildwheel project via codebase, issue tracker,
Maintainers
-----------
Core:
- Joe Rickerby [@joerick](https://github.com/joerick)
- Yannick Jadoul [@YannickJadoul](https://github.com/YannickJadoul)
- Matthieu Darbois [@mayeut](https://github.com/mayeut)
- Henry Schreiner [@henryiii](https://github.com/henryiii)
- Grzegorz Bokota [@Czaki](https://github.com/Czaki)
Platform maintainers:
- Russell Keith-Magee [@freakboy3742](https://github.com/freakboy3742) (iOS)
- Agriya Khetarpal [@agriyakhetarpal](https://github.com/agriyakhetarpal) (Pyodide)
- Hood Chatham [@hoodmane](https://github.com/hoodmane) (Pyodide)
- Gyeongjae Choi [@ryanking13](https://github.com/ryanking13) (Pyodide)
- Tim Felgentreff [@timfel](https://github.com/timfel) (GraalPy)
- Malcolm Smith [@mhsmith](https://github.com/mhsmith) (Android)
Credits
-------
+77 -22
View File
@@ -17,6 +17,10 @@ inputs:
description: 'Build a specific wheel only. No need for arch/platform if this is set'
required: false
default: ''
extras:
description: 'Comma-separated list of extras to install'
required: false
default: ''
branding:
icon: package
color: yellow
@@ -24,18 +28,18 @@ branding:
runs:
using: composite
steps:
# Set up a non-EOL, cibuildwheel & pipx supported Python version
- uses: actions/setup-python@v5
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
id: python
with:
python-version: "3.8 - 3.12"
python-version: "3.11 - 3.14"
update-environment: false
- id: cibw
run: |
# Install cibuildwheel
"${{ steps.python.outputs.python-path }}" -u << "EOF"
# Install cibuildwheel and build the command line
"$PYTHON" -u << "EOF"
import os
import shlex
import shutil
import sys
import venv
@@ -43,6 +47,8 @@ runs:
from pathlib import Path
from subprocess import run
EXTRAS = set(e.strip() for e in os.environ.get("INPUT_EXTRAS", "").split(",") if e.strip())
class EnvBuilder(venv.EnvBuilder):
def __init__(self):
@@ -54,39 +60,88 @@ runs:
def post_setup(self, context):
super().post_setup(context)
self.bin_path = Path(context.env_exe).parent
run([sys.executable, "-m", "pip", "--python", context.env_exe, "install", r"${{ github.action_path }}"], check=True)
install_spec = os.environ["GITHUB_ACTION_PATH"]
if EXTRAS:
install_spec += f"[{','.join(sorted(EXTRAS))}]"
run([sys.executable, "-m", "pip", "--python", context.env_exe, "install", install_spec], check=True)
print("::group::Install cibuildwheel")
venv_path = Path(r"${{ runner.temp }}") / "cibw"
venv_path = Path(os.environ["RUNNER_TEMP"]) / "cibw"
if venv_path.exists():
shutil.rmtree(venv_path)
builder = EnvBuilder()
builder.create(venv_path)
cibw_path = [path for path in builder.bin_path.glob("cibuildwheel*") if path.stem == "cibuildwheel"][0]
exposed_binaries = {"cibuildwheel"}
if "uv" in EXTRAS:
exposed_binaries.add("uv")
clean_bin_path = builder.bin_path.parent / f"{builder.bin_path.name}.clean"
clean_bin_path.mkdir()
for path in list(builder.bin_path.iterdir()):
if path.stem in exposed_binaries:
try:
os.symlink(path, clean_bin_path / path.name)
except OSError:
shutil.copy2(path, clean_bin_path / path.name)
cibw_bin = [p for p in builder.bin_path.glob("cibuildwheel*") if p.stem == "cibuildwheel"][0]
# Build the command line
cmd_args = [str(cibw_bin), os.environ["INPUT_PACKAGE_DIR"]]
if output_dir := os.environ.get("INPUT_OUTPUT_DIR"):
cmd_args += ["--output-dir", output_dir]
if config_file := os.environ.get("INPUT_CONFIG_FILE"):
cmd_args += ["--config-file", config_file]
if only := os.environ.get("INPUT_ONLY"):
cmd_args += ["--only", only]
cmd_bash = shlex.join(cmd_args)
def pwsh_quote(text):
# Wrap in single quotes and double-up any existing single quotes
return "'" + str(text).replace("'", "''") + "'"
# Prepend '& ' so PowerShell executes the quoted binary path
cmd_pwsh = "& " + " ".join(pwsh_quote(arg) for arg in cmd_args)
with open(os.environ["GITHUB_OUTPUT"], "at") as f:
f.write(f"cibw-path={cibw_path}\n")
f.write(f"prepend-path={clean_bin_path}\n")
f.write(f"cmd-bash={cmd_bash}\n")
f.write(f"cmd-pwsh={cmd_pwsh}\n")
print("::endgroup::")
EOF
shell: bash
env:
PYTHON: ${{ steps.python.outputs.python-path }}
INPUT_PACKAGE_DIR: ${{ inputs.package-dir }}
INPUT_OUTPUT_DIR: ${{ inputs.output-dir }}
INPUT_CONFIG_FILE: ${{ inputs.config-file }}
INPUT_ONLY: ${{ inputs.only }}
INPUT_EXTRAS: ${{ inputs.extras }}
# Redirecting stderr to stdout to fix interleaving issue in Actions.
- run: >
"${{ steps.cibw.outputs.cibw-path }}"
"${{ inputs.package-dir }}"
${{ inputs.output-dir != '' && format('--output-dir "{0}"', inputs.output-dir) || ''}}
${{ inputs.config-file != '' && format('--config-file "{0}"', inputs.config-file) || ''}}
${{ inputs.only != '' && format('--only "{0}"', inputs.only) || ''}}
2>&1
- run: |
export PATH="$CIBW_PREPEND_PATH:$PATH"
eval "$CIBW_CMD_BASH" 2>&1
shell: bash
if: runner.os != 'Windows'
env:
CIBW_PREPEND_PATH: ${{ steps.cibw.outputs.prepend-path }}
CIBW_CMD_BASH: ${{ steps.cibw.outputs.cmd-bash }}
# Windows needs powershell to interact nicely with Meson
- run: >
& "${{ steps.cibw.outputs.cibw-path }}"
"${{ inputs.package-dir }}"
${{ inputs.output-dir != '' && format('--output-dir "{0}"', inputs.output-dir) || ''}}
${{ inputs.config-file != '' && format('--config-file "{0}"', inputs.config-file) || ''}}
${{ inputs.only != '' && format('--only "{0}"', inputs.only) || ''}}
- run: |
$env:PATH = "$env:CIBW_PREPEND_PATH;$env:PATH"
Invoke-Expression $env:CIBW_CMD_PWSH
shell: pwsh
if: runner.os == 'Windows'
env:
CIBW_PREPEND_PATH: ${{ steps.cibw.outputs.prepend-path }}
CIBW_CMD_PWSH: ${{ steps.cibw.outputs.cmd-pwsh }}
-35
View File
@@ -1,35 +0,0 @@
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
APPVEYOR_JOB_NAME: "python38-x64-ubuntu"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
APPVEYOR_JOB_NAME: "python38-x64-vs2015"
- APPVEYOR_BUILD_WORKER_IMAGE: macos
APPVEYOR_JOB_NAME: "python38-x64-macos"
stack: python 3.8
build: off
init:
- cmd: set PATH=C:\Python38;C:\Python38\Scripts;%PATH%
- ps: |
$BRANCH = if ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) { $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH } else { $env:APPVEYOR_REPO_BRANCH }
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'
}
python -m pip install -U pip
install: python -m pip install -e ".[dev]" pytest-custom-exit-code
# the '-u' flag is required so the output is in the correct order.
# See https://github.com/pypa/cibuildwheel/pull/24 for more info.
test_script: python -u ./bin/run_tests.py
branches:
only:
- main
skip_commits:
files:
- docs/*
+65 -27
View File
@@ -1,38 +1,76 @@
pr:
paths:
exclude:
- docs/*
- .github/**
- bin/*
- docs/**
- examples/github-*
- examples/ci*
- examples/travis-ci-*
- .ci*
- .pre-commit-config.yaml
- .readthedocs.yml
- .travis.yml
- README.md
- mkdocs.yml
- noxfile.py
jobs:
- job: linux_38
timeoutInMinutes: 120
pool: {vmImage: 'Ubuntu-20.04'}
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.8'
- bash: |
python -m pip install -e ".[dev]"
python ./bin/run_tests.py
- job: macos_38
pool: {vmImage: 'macOS-11'}
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.8'
- bash: |
python -m pip install -e ".[dev]"
python ./bin/run_tests.py --num-processes 2
- job: windows_38
pool: {vmImage: 'windows-2019'}
- job: tests
strategy:
matrix:
linux_311:
imageName: "ubuntu-latest"
pythonVersion: "3.11"
testSelect: "native"
android_311:
imageName: "ubuntu-latest"
pythonVersion: "3.11"
testSelect: "android"
macos_311:
imageName: "macos-latest"
pythonVersion: "3.11"
testSelect: "native"
ios_311:
imageName: "macos-14" # See https://github.com/actions/runner-images/issues/12777
pythonVersion: "3.11"
testSelect: "ios"
android_macos_311:
imageName: "macos-latest"
pythonVersion: "3.11"
testSelect: "android"
windows_311:
imageName: "windows-latest"
pythonVersion: "3.11"
testSelect: "native"
timeoutInMinutes: 180
pool:
vmImage: $(imageName)
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.8'
versionSpec: $(pythonVersion)
- task: JavaToolInstaller@0
condition: and(eq(variables['testSelect'], 'android'), eq(variables['Agent.OS'], 'Linux'))
inputs:
versionSpec: '17'
jdkArchitectureOption: 'x64'
jdkSourceOption: 'PreInstalled'
- bash: docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
condition: and(eq(variables['imageName'], 'ubuntu-latest'), eq(variables['testSelect'], 'native'))
displayName: 'Install binfmt on Linux'
- bash: python -m pip install -U pip && python -m pip install -e. --group test
displayName: 'Update pip and install cibuildwheel --group test'
- bash: echo "##vso[task.setvariable variable=CIBW_ENABLE;]all"
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
displayName: Set CIBW_ENABLE to all (main branch)
- bash: |
python -m pip install -e ".[dev]"
python ./bin/run_tests.py
echo "CIBW_ENABLE = $CIBW_ENABLE"
python ./bin/run_tests.py --test-select $(testSelect)
displayName: 'Run tests'
+9 -17
View File
@@ -1,27 +1,20 @@
#!/usr/bin/env python3
# /// script
# dependencies = ["click", "packaging", "tomli; python_version<'3.11'"]
# dependencies = ["click", "packaging", "prek"]
# ///
from __future__ import annotations
import glob
import os
import subprocess
import sys
import tomllib
import urllib.parse
from pathlib import Path
import click
from packaging.version import InvalidVersion, Version
if sys.version_info < (3, 11):
import tomli as tomllib
else:
import tomllib
config = [
# file path, version find/replace format
("pyproject.toml", 'version = "{}"'),
@@ -29,7 +22,6 @@ config = [
("cibuildwheel/__init__.py", '__version__ = "{}"'),
("docs/faq.md", "cibuildwheel=={}"),
("docs/faq.md", "cibuildwheel@v{}"),
("docs/setup.md", "cibuildwheel=={}"),
("examples/*", "cibuildwheel=={}"),
("examples/*", "cibuildwheel@v{}"),
]
@@ -76,8 +68,8 @@ def bump_version() -> None:
sys.exit(1)
# fmt: off
print( 'Current version:', current_version)
new_version = input(' New version: ').strip()
print( "Current version:", current_version)
new_version = input(" New version: ").strip()
# fmt: on
try:
@@ -90,7 +82,7 @@ def bump_version() -> None:
actions = []
for path_pattern, version_pattern in config:
paths = [Path(p) for p in glob.glob(path_pattern)]
paths = list(Path().glob(path_pattern))
if not paths:
print(f"error: Pattern {path_pattern} didn't match any files")
@@ -137,8 +129,8 @@ def bump_version() -> None:
contents = contents.replace(find, replace)
path.write_text(contents, encoding="utf8")
print("Files updated. If you want to update the changelog as part of this")
print("commit, do that now.")
print("Files updated. If you want to update docs/changelog.md as part of")
print("this commit, do that now.")
print()
while input('Type "done" to continue: ').strip().lower() != "done":
@@ -147,7 +139,7 @@ def bump_version() -> None:
# run pre-commit to update the README changelog
subprocess.run(
[
"pre-commit",
"prek",
"run",
"--files=docs/changelog.md",
],
@@ -157,7 +149,7 @@ def bump_version() -> None:
# run pre-commit to check that no errors occurred on the second run
subprocess.run(
[
"pre-commit",
"prek",
"run",
"--files=docs/changelog.md",
],
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
import sys
import textwrap
from pathlib import Path
import click
from cibuildwheel.extra import get_pyodide_xbuildenv_info
@click.command()
@click.argument(
"pyodide-version",
type=str,
)
@click.option(
"--output-file",
type=click.Path(),
default=None,
help="Output file to write the constraints to. If not provided, the constraints will be printed to stdout.",
)
def generate_pyodide_constraints(pyodide_version: str, output_file: str | None = None) -> None:
"""
Generate constraints for a specific Pyodide version. The constraints are
generated based on the Pyodide version's xbuildenv info, which is retrieved
from the Pyodide repository.
These constraints should then be 'pinned' using `uv pip compile`.
Example usage:
bin/generate_pyodide_constraints.py 0.27.0
"""
xbuildenv_info = get_pyodide_xbuildenv_info()
try:
pyodide_version_xbuildenv_info = xbuildenv_info["releases"][pyodide_version]
except KeyError as e:
msg = f"Pyodide version {pyodide_version} not found in xbuildenv info. Versions available: {', '.join(xbuildenv_info['releases'].keys())}"
raise click.BadParameter(msg) from e
pyodide_build_min_version = pyodide_version_xbuildenv_info.get("min_pyodide_build_version")
pyodide_build_max_version = pyodide_version_xbuildenv_info.get("max_pyodide_build_version")
pyodide_build_specifier_parts: list[str] = []
if pyodide_build_min_version:
pyodide_build_specifier_parts.append(f">={pyodide_build_min_version}")
if pyodide_build_max_version:
pyodide_build_specifier_parts.append(f"<={pyodide_build_max_version}")
pyodide_build_specifier = ",".join(pyodide_build_specifier_parts)
constraints_txt = textwrap.dedent(f"""
pip
build[virtualenv]
pyodide-build{pyodide_build_specifier}
click<8.2
""")
if output_file is None:
print(constraints_txt)
else:
Path(output_file).write_text(constraints_txt)
print(f"Constraints written to {output_file}", file=sys.stderr)
if __name__ == "__main__":
generate_pyodide_constraints()
+100 -16
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env -S uv run -q
# /// script
# dependencies = ["pyyaml"]
@@ -11,12 +11,12 @@ from typing import Any
import yaml
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument("--schemastore", action="store_true", help="Generate schema_store version")
args = parser.parse_args()
starter = """
$schema: http://json-schema.org/draft-07/schema
$schema: http://json-schema.org/draft-07/schema#
$id: https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/resources/cibuildwheel.schema.json
$defs:
inherit:
@@ -26,6 +26,15 @@ $defs:
- append
default: none
description: How to inherit the parent's value.
enable:
enum:
- cpython-freethreading
- cpython-prerelease
- graalpy
- pyodide-prerelease
- pypy
- pypy-eol
description: A Python version or flavor to enable.
additionalProperties: false
description: cibuildwheel's settings.
type: object
@@ -48,19 +57,23 @@ properties:
type: string_array
build-frontend:
default: default
description: Set the tool to use to build, either "pip" (default for now) or "build"
description: Set the tool to use to build, either "build" (default), "build[uv]", "uv", or "pip"
oneOf:
- enum: [pip, build, default]
- enum: [pip, build, "build[uv]", uv, default]
- type: string
pattern: '^pip; ?args:'
- type: string
pattern: '^build; ?args:'
- type: string
pattern: '^build\\[uv\\]; ?args:'
- type: string
pattern: '^uv; ?args:'
- type: object
additionalProperties: false
required: [name]
properties:
name:
enum: [pip, build]
enum: [pip, build, "build[uv]", uv]
args:
type: array
items:
@@ -96,7 +109,31 @@ properties:
dependency-versions:
default: pinned
description: Specify how cibuildwheel controls the versions of the tools it uses
type: string
oneOf:
- enum: [pinned, latest]
- type: string
description: Path to a file containing dependency versions, or inline package specifications, starting with "packages:"
not:
enum: [pinned, latest]
- type: object
additionalProperties: false
properties:
file:
type: string
- type: object
additionalProperties: false
properties:
packages:
type: array
items:
type: string
enable:
description: Enable or disable certain builds.
oneOf:
- $ref: "#/$defs/enable"
- type: array
items:
$ref: "#/$defs/enable"
environment:
description: Set environment variables needed during the build.
type: string_table
@@ -104,13 +141,12 @@ properties:
description: Set environment variables on the host to pass-through to the container
during the build.
type: string_array
free-threaded-support:
type: boolean
default: false
description: The project supports free-threaded builds of Python (PEP703)
manylinux-aarch64-image:
type: string
description: Specify alternative manylinux / musllinux container images
manylinux-armv7l-image:
type: string
description: Specify alternative manylinux / musllinux container images
manylinux-i686-image:
type: string
description: Specify alternative manylinux / musllinux container images
@@ -126,6 +162,9 @@ properties:
manylinux-pypy_x86_64-image:
type: string
description: Specify alternative manylinux / musllinux container images
manylinux-riscv64-image:
type: string
description: Specify alternative manylinux / musllinux container images
manylinux-s390x-image:
type: string
description: Specify alternative manylinux / musllinux container images
@@ -135,21 +174,33 @@ properties:
musllinux-aarch64-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-armv7l-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-i686-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-ppc64le-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-riscv64-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-s390x-image:
type: string
description: Specify alternative manylinux / musllinux container images
musllinux-x86_64-image:
type: string
description: Specify alternative manylinux / musllinux container images
repair-wheel-command:
xbuild-tools:
description: Binaries on the path that should be included in an isolated cross-build environment
type: string_array
pyodide-version:
type: string
description: Specify the version of Pyodide to use
repair-wheel-command:
description: Execute a shell command to repair each built wheel.
type: string_array
skip:
description: Choose the Python versions to skip.
type: string_array
@@ -159,12 +210,39 @@ properties:
test-extras:
description: Install your wheel for testing using `extras_require`
type: string_array
test-sources:
description: Test files that are required by the test environment
type: string_array
test-groups:
description: Install extra groups when testing
type: string_array
test-requires:
description: Install Python dependencies before running the tests
type: string_array
test-skip:
description: Skip running tests on some builds.
type: string_array
test-environment:
description: Set environment variables for the test environment
type: string_table
test-runtime:
description: Additional configuration for the test runner
oneOf:
- type: string
pattern: '^$'
- type: object
additionalProperties: false
- type: string
pattern: 'args:'
- type: object
additionalProperties: false
required: [args]
properties:
args:
type: array
items:
type: string
"""
schema = yaml.safe_load(starter)
@@ -233,6 +311,7 @@ items:
properties:
before-all: {"$ref": "#/$defs/inherit"}
before-build: {"$ref": "#/$defs/inherit"}
xbuild-tools: {"$ref": "#/$defs/inherit"}
before-test: {"$ref": "#/$defs/inherit"}
config-settings: {"$ref": "#/$defs/inherit"}
container-engine: {"$ref": "#/$defs/inherit"}
@@ -241,18 +320,21 @@ items:
repair-wheel-command: {"$ref": "#/$defs/inherit"}
test-command: {"$ref": "#/$defs/inherit"}
test-extras: {"$ref": "#/$defs/inherit"}
test-sources: {"$ref": "#/$defs/inherit"}
test-requires: {"$ref": "#/$defs/inherit"}
test-environment: {"$ref": "#/$defs/inherit"}
test-runtime: {"$ref": "#/$defs/inherit"}
"""
)
for key, value in schema["properties"].items():
value["title"] = f'CIBW_{key.replace("-", "_").upper()}'
value["title"] = f"CIBW_{key.replace('-', '_').upper()}"
non_global_options = {k: {"$ref": f"#/properties/{k}"} for k in schema["properties"]}
del non_global_options["build"]
del non_global_options["skip"]
del non_global_options["test-skip"]
del non_global_options["free-threaded-support"]
del non_global_options["enable"]
overrides["items"]["properties"]["select"]["oneOf"] = string_array
overrides["items"]["properties"] |= non_global_options.copy()
@@ -280,6 +362,9 @@ oses = {
"linux": as_object(non_global_options),
"windows": as_object(not_linux),
"macos": as_object(not_linux),
"pyodide": as_object(not_linux),
"android": as_object(not_linux),
"ios": as_object(not_linux),
}
oses["linux"]["properties"]["repair-wheel-command"] = {
@@ -298,9 +383,8 @@ schema["properties"] |= oses
if args.schemastore:
schema["$id"] = "https://json.schemastore.org/partial-cibuildwheel.json"
schema["$schema"] = "http://json-schema.org/draft-07/schema#"
schema["description"] = (
"cibuildwheel's toml file, generated with ./bin/generate_schema.py --schemastore from cibuildwheel."
"cibuildwheel's settings. Generated with ./bin/generate_schema.py --schemastore from cibuildwheel."
)
print(json.dumps(schema, indent=2))
-2
View File
@@ -11,8 +11,6 @@ This will cache the results to all_known_setup.yaml; you can reprint
the results without the `--online` setting.
"""
from __future__ import annotations
import ast
from collections.abc import Iterable, Iterator
from pathlib import Path
+11 -10
View File
@@ -1,29 +1,30 @@
#!/usr/bin/env python3
from __future__ import annotations
import os
import subprocess
import sys
import textwrap
import time
from pathlib import Path
from subprocess import run
import click
def shell(cmd, *, check: bool, **kwargs):
return run([cmd], shell=True, check=check, **kwargs)
def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]
def git_repo_has_changes():
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
def git_repo_has_changes() -> bool:
unstaged_changes: bool = shell("git diff-index --quiet 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
@click.command()
def main():
def main() -> None:
project_root = Path(__file__).parent / ".."
os.chdir(project_root)
@@ -54,10 +55,10 @@ def main():
f"""
Update the versions of our dependencies.
PR generated by `{os.path.basename(__file__)}`.
PR generated by `{Path(__file__).name}`.
"""
)
run(
subprocess.run(
[
"gh",
"pr",
+13 -13
View File
@@ -17,25 +17,22 @@ Suggested usage:
git diff
"""
from __future__ import annotations
import builtins
import functools
import textwrap
import urllib.request
import xml.dom.minidom
from collections.abc import Iterable, Mapping, Sequence
from datetime import datetime
from datetime import UTC, datetime
from io import StringIO
from pathlib import Path
from typing import Any, TextIO
from typing import Any, Self, TextIO
import click
import yaml
from github import Github, GithubException
ICONS = (
"appveyor",
"github",
"azurepipelines",
"circleci",
@@ -80,12 +77,12 @@ class Project:
self.notes = notes
else:
self.num_stars = 0
self.pushed_at = datetime.utcnow()
self.pushed_at = datetime.now(UTC)
name_len = len(self.name) + 4
self.__class__.NAME = max(self.__class__.NAME, name_len)
def __lt__(self, other: Project) -> bool:
def __lt__(self, other: Self) -> bool:
if self.online:
return self.num_stars < other.num_stars
else:
@@ -95,8 +92,8 @@ class Project:
def header(cls) -> str:
return textwrap.dedent(
f"""\
| {'Name':{cls.NAME}} | CI | OS | Notes |
|{'':-^{cls.NAME+2 }}|----|----|:------|"""
| {"Name":{cls.NAME}} | CI | OS | Notes |
|{"":-^{cls.NAME + 2}}|----|----|:------|"""
)
@property
@@ -127,7 +124,7 @@ class Project:
return f"[{self.name}]: {self.url}"
def info(self) -> str:
days = (datetime.utcnow() - self.pushed_at).days
days = (datetime.now(UTC) - self.pushed_at).days
return f"<!-- {self.name}: {self.num_stars}, last pushed {days} days ago -->"
@@ -138,6 +135,7 @@ def fetch_icon(icon_name: str) -> None:
document = xml.dom.minidom.parseString(original_svg_data)
svgElement = document.documentElement
assert svgElement is not None
assert svgElement.nodeName == "svg"
svgElement.setAttribute("width", "16px")
svgElement.setAttribute("fill", "#606060")
@@ -172,7 +170,9 @@ def get_projects(
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()
print = functools.partial(builtins.print, file=io)
@@ -203,7 +203,7 @@ def insert_projects_table(
projects: Sequence[Project],
input_filename: str,
include_info: bool = True,
):
) -> None:
text = file.read_text()
projects_table = render_projects(projects, include_info=include_info, dest_path=file)
@@ -217,7 +217,7 @@ def insert_projects_table(
generated_note = f"<!-- this section is generated by bin/projects.py. Don't edit it directly, instead, edit {input_filename} -->"
new_text = (
f"{text[:start + len(start_str)]}\n{generated_note}\n\n{projects_table}\n{text[end:]}"
f"{text[: start + len(start_str)]}\n{generated_note}\n\n{projects_table}\n{text[end:]}"
)
file.write_text(new_text)
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import re
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent / ".."
CHANGELOG_FILE = PROJECT_ROOT / "docs" / "changelog.md"
# https://regexr.com/622ds
FIRST_5_CHANGELOG_ENTRIES_REGEX = re.compile(r"""(^###.*?(?=###)){5}""", re.DOTALL | re.MULTILINE)
def mini_changelog() -> str:
changelog_text = CHANGELOG_FILE.read_text()
mini_changelog_match = FIRST_5_CHANGELOG_ENTRIES_REGEX.search(changelog_text)
assert mini_changelog_match, "Failed to find the first few changelog entries"
return f"\n{mini_changelog_match.group(0).strip()}\n"
if __name__ == "__main__":
print(mini_changelog())
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
import dataclasses
import re
from pathlib import Path
from typing import Final
DIR: Final[Path] = Path(__file__).parent.parent.resolve()
OPTIONS_MD: Final[Path] = DIR / "docs" / "options.md"
SECTION_HEADER_REGEX = re.compile(r"^## (?P<name>.*?)$", re.MULTILINE)
# https://regexr.com/8f1ff
OPTION_HEADER_REGEX = re.compile(
r"^### (?P<name>.*?){.*#(?P<id>\S+).*}\n+> ?(?P<desc>.*)$", re.MULTILINE
)
@dataclasses.dataclass(kw_only=True)
class Option:
name: str
id: str
desc: str
section: str
def get_table() -> str:
options_md = OPTIONS_MD.read_text(encoding="utf-8")
sections = SECTION_HEADER_REGEX.split(options_md)[1:]
options = []
for section_name, section_content in zip(sections[0::2], sections[1::2], strict=True):
for match in OPTION_HEADER_REGEX.finditer(section_content):
option = Option(
name=match.group("name").strip(),
id=match.group("id").strip(),
desc=match.group("desc").strip(),
section=section_name.strip(),
)
options.append(option)
table_md = "\n<!-- This table is auto-generated from docs/options.md by bin/readme_options_table.py -->\n\n"
table_md += "| | Option | Description |\n"
table_md += "|---|---|---|\n"
last_section: str | None = None
for option in options:
cells: list[str] = []
cells.append(f"**{option.section}**" if option.section != last_section else "")
last_section = option.section
url = f"https://cibuildwheel.pypa.io/en/stable/options/#{option.id}"
name = option.name.replace(", ", "<br>") # Replace commas with line breaks
cells.append(f"[{name}]({url})")
cells.append(option.desc)
table_md += "| " + " | ".join(cells) + " |\n"
table_md += "\n"
return table_md
if __name__ == "__main__":
print(get_table())
+87 -35
View File
@@ -1,36 +1,45 @@
#!/usr/bin/env python3
from __future__ import annotations
import os
import re
import shutil
import subprocess
import sys
import textwrap
import time
import typing
from glob import glob
from pathlib import Path
from subprocess import run
from urllib.parse import quote
import click
DIR = Path(__file__).parent.parent.resolve()
def shell(cmd, *, check: bool, **kwargs):
return run([cmd], shell=True, check=check, **kwargs)
BuildBackend = typing.Literal["setuptools", "meson"]
def git_repo_has_changes():
def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]
def git_repo_has_changes() -> bool:
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
return unstaged_changes or staged_changes
def generate_basic_project(path):
def generate_project(path: Path, build_backend: BuildBackend) -> None:
sys.path.insert(0, "")
from test.test_projects.c import new_c_project
match build_backend:
case "meson":
from test.test_projects.meson import new_meson_project as new_project # noqa: PLC0415
case "setuptools":
from test.test_projects.setuptools import new_c_project as new_project # noqa: PLC0415
case _:
typing.assert_never(build_backend)
project = new_c_project()
project = new_project()
project.generate(path)
@@ -38,14 +47,48 @@ class CIService(typing.NamedTuple):
name: str
dst_config_path: str
badge_md: str
config_file_transform: typing.Callable[[str, str], str] = lambda x, _: x # identity by default
def github_config_file_transform(content: str, git_ref: str) -> str:
# one of the the github configs only builds on main, so we need to remove that restriction
# so our example build will run on the test branch.
#
# replace:
# """
# push:
# branches:
# - main
# """
# with:
# """
# push:
# """"
content = re.sub(
r"push:\n\s+branches:\n\s+- main",
"push:",
content,
)
# use the version of cibuildwheel from the current commit, not the latest
# release
# replace:
# """
# uses: pypa/cibuildwheel@v3.3.1
# """
# with:
# """
# uses: pypa/cibuildwheel@<latest commit hash>
# """
content = re.sub(
r"uses: pypa/cibuildwheel@v.*",
f"uses: pypa/cibuildwheel@{git_ref}",
content,
)
return content
services = [
CIService(
name="appveyor",
dst_config_path="appveyor.yml",
badge_md="[![Build status](https://ci.appveyor.com/api/projects/status/gt3vwl88yt0y3hur/branch/{branch}?svg=true)](https://ci.appveyor.com/project/joerick/cibuildwheel/branch/{branch})",
),
CIService(
name="azure-pipelines",
dst_config_path="azure-pipelines.yml",
@@ -59,7 +102,8 @@ services = [
CIService(
name="github",
dst_config_path=".github/workflows/example.yml",
badge_md="[![Build](https://github.com/pypa/cibuildwheel/workflows/Build/badge.svg?branch={branch})](https://github.com/pypa/cibuildwheel/actions)",
badge_md="[![Build](https://github.com/pypa/cibuildwheel/actions/workflows/example.yml/badge.svg?branch={branch})](https://github.com/pypa/cibuildwheel/actions?query=branch%3A{branch})",
config_file_transform=github_config_file_transform,
),
CIService(
name="travis-ci",
@@ -79,9 +123,8 @@ services = [
]
def ci_service_for_config_file(config_file):
filename = Path(config_file).name
def ci_service_for_config_file(config_file: Path) -> CIService:
filename = config_file.name
try:
return next(s for s in services if filename.startswith(s.name))
except StopIteration:
@@ -91,23 +134,28 @@ def ci_service_for_config_file(config_file):
@click.command()
@click.argument("config_files", nargs=-1, type=click.Path())
def run_example_ci_configs(config_files=None):
@click.option("--build-backend", type=click.Choice(["setuptools", "meson"]), default="setuptools")
def run_example_ci_configs(
config_files: list[str], build_backend: BuildBackend = "setuptools"
) -> None:
"""
Test the example configs. If no files are specified, will test
examples/*-minimal.yml
"""
if len(config_files) == 0:
config_files = glob("examples/*-minimal.yml")
config_file_paths = list(Path("examples").glob("*-minimal.yml"))
else:
config_file_paths = [Path(f) for f in config_files]
# check each CI service has at most 1 config file
configs_by_service = {}
for config_file in config_files:
configs_by_service = set()
for config_file in config_file_paths:
service = ci_service_for_config_file(config_file)
if service.name in configs_by_service:
msg = "You cannot specify more than one config per CI service"
raise Exception(msg)
configs_by_service[service.name] = config_file
configs_by_service.add(service.name)
if git_repo_has_changes():
print("Your git repo has uncommitted changes. Commit or stash before continuing.")
@@ -116,6 +164,9 @@ def run_example_ci_configs(config_files=None):
previous_branch = shell(
"git rev-parse --abbrev-ref HEAD", check=True, capture_output=True, encoding="utf8"
).stdout.strip()
git_ref = shell(
"git rev-parse HEAD", check=True, capture_output=True, encoding="utf8"
).stdout.strip()
timestamp = time.strftime("%Y-%m-%dT%H-%M-%S", time.gmtime())
branch_name = f"example-config-test---{previous_branch}-{timestamp}"
@@ -124,27 +175,29 @@ def run_example_ci_configs(config_files=None):
shell(f"git checkout --orphan {branch_name}", check=True)
example_project = Path("example_root")
generate_basic_project(example_project)
generate_project(example_project, build_backend=build_backend)
for config_file in config_files:
for config_file in config_file_paths:
service = ci_service_for_config_file(config_file)
src_config_file = Path(config_file)
dst_config_file = example_project / service.dst_config_path
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(src_config_file, dst_config_file)
run(["git", "add", example_project], check=True)
contents = config_file.read_text(encoding="utf8")
contents = service.config_file_transform(contents, git_ref)
dst_config_file.write_text(contents, encoding="utf8")
subprocess.run(["git", "add", example_project], check=True)
message = textwrap.dedent(
f"""\
Test example minimal configs
Test example CI configs
Testing files: {config_files}
Testing files: {[str(f) for f in config_files]}
Generated from branch: {previous_branch}
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)
print("---")
@@ -155,14 +208,14 @@ def run_example_ci_configs(config_files=None):
print("> ")
print("> | Service | Config | Status |")
print("> |---|---|---|")
for config_file in config_files:
for config_file in config_file_paths:
service = ci_service_for_config_file(config_file)
badge = service.badge_md.format(
branch=branch_name, branch_escaped=quote(branch_name, safe="")
)
print(f"> | {service.name} | `{config_file}` | {badge} |")
print("> ")
print("> Generated by `bin/run_example_ci_config.py`")
print(f"> Generated by `{' '.join(sys.argv)}`")
print()
print("---")
finally:
@@ -174,6 +227,5 @@ def run_example_ci_configs(config_files=None):
if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
os.chdir("..")
os.chdir(DIR)
run_example_ci_configs(standalone_mode=True)
+75 -5
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
@@ -9,8 +8,12 @@ import sys
from pathlib import Path
if __name__ == "__main__":
default_cpu_count = os.cpu_count() or 2
parser = argparse.ArgumentParser()
if sys.version_info < (3, 13):
default_cpu_count = os.cpu_count() or 2
else:
default_cpu_count = os.process_cpu_count() or 2
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument(
"--run-podman", action="store_true", default=False, help="run podman tests (linux only)"
)
@@ -20,15 +23,46 @@ if __name__ == "__main__":
default=default_cpu_count,
help="number of processes to use for testing",
)
parser.add_argument(
"--test-select",
choices={"all", "native", "android", "ios", "pyodide"},
default="all",
help="Either 'native' or 'android'/'ios'/'pyodide'",
)
args = parser.parse_args()
# move cwd to the project root
os.chdir(Path(__file__).resolve().parents[1])
# doc tests
doc_test_args = [sys.executable, "-m", "pytest", "cibuildwheel"]
print(
"\n\n================================== DOC TESTS ==================================",
flush=True,
)
result = subprocess.run(doc_test_args, check=False)
if result.returncode not in (0, 5):
# Allow case where no doctests are collected (returncode 5) because
# circleci sets an explicit "-k" filter that disables doctests. There
# isn't a pattern that will only select doctests. This can be removed
# and have check=True if the circleci PYTEST_ADDOPTS is removed.
raise subprocess.CalledProcessError(
result.returncode, result.args, output=result.stdout, stderr=result.stderr
)
# unit tests
print(
"\n\n================================== UNIT TESTS ==================================",
flush=True,
)
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"
and args.test_select in ["all", "native"]
):
# run the docker unit tests only on Linux
unit_test_args += ["--run-docker"]
@@ -37,17 +71,53 @@ if __name__ == "__main__":
subprocess.run(unit_test_args, check=True)
# integration tests
print(
"\n\n=========================== SERIAL INTEGRATION TESTS ===========================",
flush=True,
)
match args.test_select:
case "all":
marks = []
case "native":
marks = ["not pyodide", "not android", "not ios"]
case mark:
marks = [f"{mark}"]
# Run the serial integration tests without multiple processes
serial_integration_test_args = [
sys.executable,
"-m",
"pytest",
"-m",
f"{' and '.join(['serial', *marks])}",
"-x",
"--durations",
"0",
"--timeout=2400",
"test",
"-vv",
]
subprocess.run(serial_integration_test_args, check=True)
print(
"\n\n========================= NON-SERIAL INTEGRATION TESTS =========================",
flush=True,
)
integration_test_args = [
sys.executable,
"-m",
"pytest",
"-m",
f"{' and '.join(['not serial', *marks])}",
f"--numprocesses={args.num_processes}",
"-x",
"--durations",
"0",
"--timeout=2400",
"test",
"-vv",
]
if sys.platform.startswith("linux") and args.run_podman:
+1 -2
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
@@ -13,7 +12,7 @@ if __name__ == "__main__":
# move cwd to the project root
os.chdir(Path(__file__).resolve().parents[1])
parser = argparse.ArgumentParser(description="Runs a sample build")
parser = argparse.ArgumentParser(description="Runs a sample build", allow_abbrev=False)
parser.add_argument("project_python_path", nargs="?", default="test.test_0_basic.basic_project")
options = parser.parse_args()
+83 -51
View File
@@ -1,70 +1,84 @@
#!/usr/bin/env python3
from __future__ import annotations
import configparser
from dataclasses import dataclass
import dataclasses
from pathlib import Path
import requests
from packaging.version import Version
DIR = Path(__file__).parent.resolve()
RESOURCES = DIR.parent / "cibuildwheel/resources"
@dataclass(frozen=True)
@dataclasses.dataclass(frozen=True)
class Image:
manylinux_version: str
platform: str
platforms: list[str]
image_name: str
tag: str | None # Set this to pin the image
tag: str | None = None # Set this to pin the image
use_platform_suffix: bool = False
class PyPAImage(Image):
def __init__(self, manylinux_version: str, platforms: list[str], tag: str | None = None):
image_name = f"quay.io/pypa/{manylinux_version}"
super().__init__(manylinux_version, platforms, image_name, tag, True)
images = [
# manylinux1 images
Image("manylinux1", "x86_64", "quay.io/pypa/manylinux1_x86_64", None),
Image("manylinux1", "i686", "quay.io/pypa/manylinux1_i686", None),
# manylinux2010 images
Image("manylinux2010", "x86_64", "quay.io/pypa/manylinux2010_x86_64", None),
Image("manylinux2010", "i686", "quay.io/pypa/manylinux2010_i686", None),
Image("manylinux2010", "pypy_x86_64", "quay.io/pypa/manylinux2010_x86_64", None),
Image("manylinux2010", "pypy_i686", "quay.io/pypa/manylinux2010_i686", None),
# manylinux2014 images
Image("manylinux2014", "x86_64", "quay.io/pypa/manylinux2014_x86_64", None),
Image("manylinux2014", "i686", "quay.io/pypa/manylinux2014_i686", None),
Image("manylinux2014", "aarch64", "quay.io/pypa/manylinux2014_aarch64", None),
Image("manylinux2014", "ppc64le", "quay.io/pypa/manylinux2014_ppc64le", None),
Image("manylinux2014", "s390x", "quay.io/pypa/manylinux2014_s390x", None),
Image("manylinux2014", "pypy_x86_64", "quay.io/pypa/manylinux2014_x86_64", None),
Image("manylinux2014", "pypy_i686", "quay.io/pypa/manylinux2014_i686", None),
Image("manylinux2014", "pypy_aarch64", "quay.io/pypa/manylinux2014_aarch64", None),
# manylinux_2_24 images
Image("manylinux_2_24", "x86_64", "quay.io/pypa/manylinux_2_24_x86_64", None),
Image("manylinux_2_24", "i686", "quay.io/pypa/manylinux_2_24_i686", None),
Image("manylinux_2_24", "aarch64", "quay.io/pypa/manylinux_2_24_aarch64", None),
Image("manylinux_2_24", "ppc64le", "quay.io/pypa/manylinux_2_24_ppc64le", None),
Image("manylinux_2_24", "s390x", "quay.io/pypa/manylinux_2_24_s390x", None),
Image("manylinux_2_24", "pypy_x86_64", "quay.io/pypa/manylinux_2_24_x86_64", None),
Image("manylinux_2_24", "pypy_i686", "quay.io/pypa/manylinux_2_24_i686", None),
Image("manylinux_2_24", "pypy_aarch64", "quay.io/pypa/manylinux_2_24_aarch64", None),
PyPAImage(
"manylinux2014",
[
"x86_64",
"i686",
"aarch64",
"ppc64le",
"s390x",
"pypy_x86_64",
"pypy_i686",
"pypy_aarch64",
],
),
# manylinux_2_28 images
Image("manylinux_2_28", "x86_64", "quay.io/pypa/manylinux_2_28_x86_64", None),
Image("manylinux_2_28", "aarch64", "quay.io/pypa/manylinux_2_28_aarch64", None),
Image("manylinux_2_28", "ppc64le", "quay.io/pypa/manylinux_2_28_ppc64le", 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_aarch64", "quay.io/pypa/manylinux_2_28_aarch64", None),
# musllinux_1_1 images
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", "aarch64", "quay.io/pypa/musllinux_1_1_aarch64", None),
Image("musllinux_1_1", "ppc64le", "quay.io/pypa/musllinux_1_1_ppc64le", None),
Image("musllinux_1_1", "s390x", "quay.io/pypa/musllinux_1_1_s390x", None),
PyPAImage(
"manylinux_2_28",
[
"x86_64",
"i686",
"aarch64",
"ppc64le",
"s390x",
"pypy_x86_64",
"pypy_i686",
"pypy_aarch64",
],
),
# manylinux_2_31 images
PyPAImage("manylinux_2_31", ["armv7l"]),
# manylinux_2_34 images
PyPAImage(
"manylinux_2_34",
[
"x86_64",
"i686",
"aarch64",
"ppc64le",
"s390x",
"pypy_x86_64",
"pypy_i686",
"pypy_aarch64",
],
),
# manylinux_2_35 images
PyPAImage("manylinux_2_35", ["armv7l"]),
# manylinux_2_39 images
PyPAImage("manylinux_2_39", ["riscv64"]),
# musllinux_1_2 images
Image("musllinux_1_2", "x86_64", "quay.io/pypa/musllinux_1_2_x86_64", None),
Image("musllinux_1_2", "i686", "quay.io/pypa/musllinux_1_2_i686", 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", "s390x", "quay.io/pypa/musllinux_1_2_s390x", None),
PyPAImage(
"musllinux_1_2", ["x86_64", "i686", "aarch64", "ppc64le", "s390x", "armv7l", "riscv64"]
),
]
config = configparser.ConfigParser()
@@ -90,6 +104,21 @@ for image in images:
for (name, info) in tags_dict.items()
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:
response = requests.get(f"https://hub.docker.com/v2/repositories/{image.image_name}/tags")
response.raise_for_status()
@@ -105,10 +134,13 @@ for image in images:
)
tag_name = pinned_tag["name"]
if not config.has_section(image.platform):
config[image.platform] = {}
config[image.platform][image.manylinux_version] = f"{image.image_name}:{tag_name}"
for platform in image.platforms:
if not config.has_section(platform):
config[platform] = {}
suffix = ""
if image.use_platform_suffix:
suffix = f"_{platform.removeprefix('pypy_')}"
config[platform][image.manylinux_version] = f"{image.image_name}{suffix}:{tag_name}"
with open(RESOURCES / "pinned_docker_images.cfg", "w") as f:
config.write(f)
+2 -4
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
from __future__ import annotations
import subprocess
import sys
@@ -18,7 +17,7 @@ except ImportError:
)
def main():
def main() -> None:
subprocess.run(["mkdocs", "build"], check=True)
hti = Html2Image(custom_flags=["--force-device-scale-factor=2"])
@@ -42,8 +41,7 @@ def main():
)
dest_path = Path("docs/data/how-it-works.png")
if dest_path.exists():
dest_path.unlink()
dest_path.unlink(missing_ok=True)
Path(screenshot).rename(dest_path)
+146
View File
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
import dataclasses
import difflib
import logging
import tomllib
from pathlib import Path
from typing import Final
import click
import packaging.specifiers
import requests
import rich
from packaging.version import InvalidVersion, Version
from rich.logging import RichHandler
from rich.syntax import Syntax
log = logging.getLogger("cibw")
# Looking up the dir instead of using utils.resources_dir
# since we want to write to it.
DIR: Final[Path] = Path(__file__).parent.parent.resolve()
RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources"
NODEJS_DIST: Final[str] = "https://nodejs.org/dist/"
NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json"
@dataclasses.dataclass(frozen=True, order=True)
class VersionTuple:
version: Version
version_string: str
def parse_nodejs_index() -> list[VersionTuple]:
versions: list[VersionTuple] = []
response = requests.get(NODEJS_INDEX)
response.raise_for_status()
versions_info = response.json()
for version_info in versions_info:
version_string = version_info.get("version", "???")
if not version_info.get("lts", False):
log.debug("Ignoring non LTS release %r", version_string)
continue
if "linux-x64" not in version_info.get("files", []):
log.warning(
"Ignoring release %r which does not include a linux-x64 binary", version_string
)
continue
try:
version = Version(version_string)
if version.is_devrelease:
log.info("Ignoring development release %r", str(version))
continue
if version.is_prerelease:
log.info("Ignoring pre-release %r", str(version))
continue
versions.append(VersionTuple(version, version_string))
except InvalidVersion:
log.warning("Ignoring release %r", version_string)
versions.sort(reverse=True)
return versions
@click.command()
@click.option("--force", is_flag=True)
@click.option(
"--level", default="INFO", type=click.Choice(["WARNING", "INFO", "DEBUG"], case_sensitive=False)
)
def update_nodejs(force: bool, level: str) -> None:
logging.basicConfig(
level="INFO",
format="%(message)s",
datefmt="[%X]",
handlers=[RichHandler(rich_tracebacks=True, markup=True)],
)
log.setLevel(level)
toml_file_path = RESOURCES_DIR / "nodejs.toml"
original_toml = toml_file_path.read_text()
with toml_file_path.open("rb") as f:
nodejs_data = tomllib.load(f)
nodejs_data.pop("url")
major_versions = [VersionTuple(Version(key), key) for key in nodejs_data]
major_versions.sort(reverse=True)
versions = parse_nodejs_index()
# update existing versions, 1 per LTS
for major_version in major_versions:
current = Version(nodejs_data[major_version.version_string])
specifier = packaging.specifiers.SpecifierSet(
specifiers=f"=={major_version.version.major}.*"
)
for version in versions:
if specifier.contains(version.version) and version.version > current:
nodejs_data[major_version.version_string] = version.version_string
break
# check for a new major LTS to insert
if versions and versions[0].version.major > major_versions[0].version.major:
major_versions.insert(
0,
VersionTuple(Version(str(versions[0].version.major)), f"v{versions[0].version.major}"),
)
nodejs_data[major_versions[0].version_string] = versions[0].version_string
versions_toml = "\n".join(
f'{major_version.version_string} = "{nodejs_data[major_version.version_string]}"'
for major_version in major_versions
)
result_toml = f'url = "{NODEJS_DIST}"\n{versions_toml}\n'
rich.print() # spacer
if original_toml == result_toml:
rich.print("[green]Check complete, nodejs version unchanged.")
return
rich.print("nodejs version updated.")
rich.print("Changes:")
rich.print()
toml_relpath = toml_file_path.relative_to(DIR).as_posix()
diff_lines = difflib.unified_diff(
original_toml.splitlines(keepends=True),
result_toml.splitlines(keepends=True),
fromfile=toml_relpath,
tofile=toml_relpath,
)
rich.print(Syntax("".join(diff_lines), "diff", theme="ansi_light"))
rich.print()
if force:
toml_file_path.write_text(result_toml)
rich.print("[green]TOML file updated.")
else:
rich.print("[yellow]File left unchanged. Use --force flag to update.")
if __name__ == "__main__":
update_nodejs()
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
import json
from cibuildwheel.extra import github_api_request
from cibuildwheel.util.python_build_standalone import (
PythonBuildStandaloneAsset,
PythonBuildStandaloneReleaseData,
)
from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES
def main() -> None:
"""
This script updates the vendored list of release assets to the latest
version of astral-sh/python-build-standalone.
"""
# Get the latest release tag from the GitHub API
latest_release = github_api_request("repos/astral-sh/python-build-standalone/releases/latest")
latest_tag = latest_release["tag_name"]
# Get the list of assets for the latest release
github_assets = github_api_request(
f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
)["assets"]
assets = [
PythonBuildStandaloneAsset(name=ga["name"], url=ga["browser_download_url"])
for ga in github_assets
if ga["name"].endswith("install_only.tar.gz")
]
# Try to keep output order stable
assets = sorted(assets, key=lambda x: x["name"])
# Write the assets to the JSON file. One day, we might need to support
# multiple releases, but for now, we only support the latest one
json_file_contents = PythonBuildStandaloneReleaseData(
releases=[
{
"tag": latest_tag,
"assets": assets,
}
]
)
with PYTHON_BUILD_STANDALONE_RELEASES.open("w", encoding="utf-8") as f:
json.dump(json_file_contents, f, indent=2)
# Add a trailing newline, our pre-commit hook requires it
f.write("\n")
print(
f"Updated {PYTHON_BUILD_STANDALONE_RELEASES.name} with {len(assets)} assets for tag {latest_tag}"
)
if __name__ == "__main__":
main()
+254 -58
View File
@@ -1,13 +1,15 @@
#!/usr/bin/env python3
from __future__ import annotations
import copy
import difflib
import logging
import operator
import re
import tomllib
from collections.abc import Mapping, MutableMapping
from pathlib import Path
from typing import Any, Final, Literal, TypedDict, Union
from typing import Any, Final, Literal, TypedDict
from xml.etree import ElementTree as ET
import click
import requests
@@ -17,8 +19,8 @@ from packaging.version import Version
from rich.logging import RichHandler
from rich.syntax import Syntax
from cibuildwheel._compat import tomllib
from cibuildwheel.extra import dump_python_configurations
from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info
from cibuildwheel.platforms.android import android_triplet
log = logging.getLogger("cibw")
@@ -31,26 +33,18 @@ RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources"
ArchStr = Literal["32", "64", "ARM64"]
class ConfigWinCP(TypedDict):
class Config(TypedDict):
identifier: str
version: str
arch: str
class ConfigWinPP(TypedDict):
identifier: str
version: str
arch: str
class ConfigUrl(Config):
url: str
class ConfigMacOS(TypedDict):
identifier: str
version: str
url: str
AnyConfig = Union[ConfigWinCP, ConfigWinPP, ConfigMacOS]
class ConfigPyodide(Config):
default_pyodide_version: str
node_version: str
# The following set of "Versions" classes allow the initial call to the APIs to
@@ -84,7 +78,7 @@ class WindowsVersions:
self.version_dict = {Version(v): v for v in cp_info["versions"]}
def update_version_windows(self, spec: Specifier) -> ConfigWinCP | None:
def update_version_windows(self, spec: Specifier) -> Config | None:
# Specifier.filter selects all non pre-releases that match the spec,
# unless there are only pre-releases, then it selects pre-releases
# instead (like pip)
@@ -99,13 +93,84 @@ class WindowsVersions:
flags = "t" if self.free_threaded else ""
version = versions[0]
identifier = f"cp{version.major}{version.minor}{flags}-{self.arch}"
return ConfigWinCP(
return Config(
identifier=identifier,
version=self.version_dict[version],
arch=self.arch_str,
)
class GraalPyVersions:
def __init__(self) -> None:
response = requests.get("https://api.github.com/repos/oracle/graalpython/releases")
response.raise_for_status()
releases = response.json()
gp_version_re = re.compile(r"-(\d+\.\d+\.\d+)$")
cp_version_re = re.compile(r"Python (\d+\.\d+(?:\.\d+)?)")
for release in releases:
m = gp_version_re.search(release["tag_name"])
if m:
release["graalpy_version"] = Version(m.group(1))
m = cp_version_re.search(release["body"])
if m:
release["python_version"] = Version(m.group(1))
self.releases = [r for r in releases if "graalpy_version" in r and "python_version" in r]
def update_version(self, identifier: str, spec: Specifier) -> ConfigUrl | None:
if "x86_64" in identifier or "amd64" in identifier:
arch = "x86_64"
elif "arm64" in identifier or "aarch64" in identifier:
arch = "aarch64"
else:
msg = f"{identifier} not supported yet on GraalPy"
raise RuntimeError(msg)
gpspec_str = identifier.split("-", maxsplit=1)[0].split("_")[1]
if "." not in gpspec_str and len(gpspec_str) == 3:
gpspec_str = gpspec_str[:2] + "." + gpspec_str[-1]
gpspec = Specifier(f"=={gpspec_str}.*")
releases_tmp = (r for r in self.releases if spec.contains(r["python_version"]))
releases_tmp = (r for r in releases_tmp if gpspec.contains(r["graalpy_version"]))
releases = sorted(releases_tmp, key=lambda r: r["graalpy_version"])
if not releases:
msg = f"GraalPy {arch} not found for {spec}!"
raise RuntimeError(msg)
release = releases[-1]
version = release["python_version"]
gpversion = release["graalpy_version"]
if "macosx" in identifier:
arch = "x86_64" if "x86_64" in identifier else "arm64"
platform = "macos"
elif "win" in identifier:
arch = "aarch64" if "arm64" in identifier else "x86_64"
platform = "windows"
else:
msg = "GraalPy provides downloads for macOS and Windows and is included for manylinux"
raise RuntimeError(msg)
arch = "amd64" if arch == "x86_64" else "aarch64"
ext = "zip" if "win" in identifier else "tar.gz"
urls = [
rf["browser_download_url"]
for rf in release["assets"]
if rf["name"].endswith(f"{platform}-{arch}.{ext}")
and rf["name"].startswith(f"graalpy-{gpversion.major}")
]
if urls:
(url,) = urls
return ConfigUrl(
identifier=identifier,
version=f"{version.major}.{version.minor}",
url=url,
)
return None
class PyPyVersions:
def __init__(self, arch_str: ArchStr):
response = requests.get("https://downloads.python.org/pypy/versions.json")
@@ -131,9 +196,9 @@ class PyPyVersions:
]
return urls[0] if urls else ""
def update_version_windows(self, spec: Specifier) -> ConfigWinCP:
def update_version_windows(self, spec: Specifier) -> ConfigUrl:
releases = [r for r in self.releases if spec.contains(r["python_version"])]
releases = sorted(releases, key=lambda r: r["pypy_version"])
releases = sorted(releases, key=operator.itemgetter("pypy_version"))
releases = [r for r in releases if self.get_arch_file(r)]
if not releases:
@@ -147,20 +212,19 @@ class PyPyVersions:
identifier = f"pp{version.major}{version.minor}-{version_arch}"
url = self.get_arch_file(release)
return ConfigWinPP(
return ConfigUrl(
identifier=identifier,
version=f"{version.major}.{version.minor}",
arch=self.arch,
url=url,
)
def update_version_macos(self, spec: Specifier) -> ConfigMacOS:
def update_version_macos(self, spec: Specifier) -> ConfigUrl:
if self.arch not in {"64", "ARM64"}:
msg = f"'{self.arch}' arch not supported yet on macOS"
raise RuntimeError(msg)
releases = [r for r in self.releases if spec.contains(r["python_version"])]
releases = sorted(releases, key=lambda r: r["pypy_version"])
releases = sorted(releases, key=operator.itemgetter("pypy_version"))
if not releases:
msg = f"PyPy macOS {self.arch} not found for {spec}!"
@@ -178,7 +242,7 @@ class PyPyVersions:
if "" in rf["platform"] == "darwin" and rf["arch"] == arch
)
return ConfigMacOS(
return ConfigUrl(
identifier=identifier,
version=f"{version.major}.{version.minor}",
url=url,
@@ -196,34 +260,31 @@ class CPythonVersions:
self.versions_dict: dict[Version, int] = {}
for release in releases_info:
# Removing the prefix, Python 3.9 would use: release["name"].removeprefix("Python ")
version = Version(release["name"][7:])
# Skip the pymanager releases
if not release["slug"].startswith("python"):
continue
uri = int(release["resource_uri"].rstrip("/").split("/")[-1])
self.versions_dict[version] = uri
# Removing the prefix
version = Version(release["name"].removeprefix("Python "))
self.versions_dict[version] = release["resource_uri"]
def update_version_macos(
self, identifier: str, version: Version, spec: Specifier
) -> ConfigMacOS | None:
files_response = requests.get("https://www.python.org/api/v2/downloads/release_file/")
files_response.raise_for_status()
self.files_info = files_response.json()
def update_version(self, identifier: str, spec: Specifier, file_ident: str) -> ConfigUrl | None:
# see note above on Specifier.filter
unsorted_versions = spec.filter(self.versions_dict)
sorted_versions = sorted(unsorted_versions, reverse=True)
macver = "x10.9" if version <= Version("3.8.9999") else "11"
file_ident = f"macos{macver}.pkg"
for new_version in sorted_versions:
# Find the first patch version that contains the requested file
uri = self.versions_dict[new_version]
response = requests.get(
f"https://www.python.org/api/v2/downloads/release_file/?release={uri}"
)
response.raise_for_status()
file_info = response.json()
files = [rf for rf in self.files_info if rf["release"] == uri]
urls = [rf["url"] for rf in file_info if file_ident in rf["url"]]
urls = [rf["url"] for rf in files if file_ident in rf["url"]]
if urls:
return ConfigMacOS(
return ConfigUrl(
identifier=identifier,
version=f"{new_version.major}.{new_version.minor}",
url=urls[0],
@@ -231,6 +292,120 @@ class CPythonVersions:
return None
def update_version_macos(
self, identifier: str, version: Version, spec: Specifier
) -> ConfigUrl | None:
macver = "x10.9" if version <= Version("3.8.9999") else "11"
return self.update_version(identifier, spec, f"macos{macver}.pkg")
def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl | None:
return self.update_version(identifier, spec, android_triplet(identifier))
class MavenVersions:
MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python"
def __init__(self) -> None:
response = requests.get(f"{self.MAVEN_URL}/maven-metadata.xml")
response.raise_for_status()
root = ET.fromstring(response.text)
self.versions: list[Version] = []
for version_elem in root.findall("./versioning/versions/version"):
version_str = version_elem.text
assert isinstance(version_str, str), version_str
self.versions.append(Version(version_str))
def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl | None:
sorted_versions = sorted(spec.filter(self.versions), reverse=True)
# Return a config using the highest version for the given specifier.
if sorted_versions:
max_version = sorted_versions[0]
triplet = android_triplet(identifier)
return ConfigUrl(
identifier=identifier,
version=f"{max_version.major}.{max_version.minor}",
url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz",
)
else:
return None
class CPythonIOSVersions:
def __init__(self) -> None:
response = requests.get(
"https://api.github.com/repos/beeware/Python-Apple-support/releases",
headers={
"Accept": "application/vnd.github+json",
"X-Github-Api-Version": "2022-11-28",
},
)
response.raise_for_status()
releases_info = response.json()
self.versions_dict: dict[Version, dict[int, str]] = {}
# Each release has a name like "3.13-b4"
for release in releases_info:
py_version, build = release["name"].split("-")
version = Version(py_version)
self.versions_dict.setdefault(version, {})
# There are several release assets associated with each release;
# The name of the asset will be something like
# "Python-3.11-iOS-support.b4.tar.gz". Store all builds that are
# "-iOS-support" builds, retaining the download URL.
for asset in release["assets"]:
filename, build, _, _ = asset["name"].rsplit(".", 3)
if filename.endswith("-iOS-support"):
self.versions_dict[version][int(build[1:])] = asset["browser_download_url"]
def update_version_ios(self, identifier: str, version: Version) -> ConfigUrl | None:
# Return a config using the highest build number for the given version.
urls = [url for _, url in sorted(self.versions_dict.get(version, {}).items())]
if urls:
return ConfigUrl(
identifier=identifier,
version=str(version),
url=urls[-1],
)
return None
class PyodideVersions:
def __init__(self) -> None:
xbuildenv_info = get_pyodide_xbuildenv_info()
self.releases = xbuildenv_info["releases"]
def update_version_pyodide(
self, identifier: str, version: Version, spec: Specifier, node_version: str
) -> ConfigPyodide | None:
# get releases that match the python version
releases = [
r for r in self.releases.values() if spec.contains(Version(r["python_version"]))
]
# sort by version, latest first
releases.sort(key=lambda r: Version(r["version"]), reverse=True)
if not releases:
msg = f"Pyodide not found for {spec}!"
raise ValueError(msg)
final_releases = [r for r in releases if not Version(r["version"]).is_prerelease]
# prefer a final release if available, otherwise use the latest
# pre-release
release = final_releases[0] if final_releases else releases[0]
return ConfigPyodide(
identifier=identifier,
version=str(version),
default_pyodide_version=release["version"],
node_version=node_version,
)
# This is a universal interface to all the above Versions classes. Given an
# identifier, it updates a config dict.
@@ -246,27 +421,37 @@ class AllVersions:
self.windows_t_arm64 = WindowsVersions("ARM64", True)
self.windows_pypy_64 = PyPyVersions("64")
self.macos_cpython = CPythonVersions()
self.cpython = CPythonVersions()
self.macos_pypy = PyPyVersions("64")
self.macos_pypy_arm64 = PyPyVersions("ARM64")
self.maven = MavenVersions()
self.ios_cpython = CPythonIOSVersions()
self.graalpy = GraalPyVersions()
self.pyodide = PyodideVersions()
def update_config(self, config: MutableMapping[str, str]) -> None:
identifier = config["identifier"]
version = Version(config["version"])
spec = Specifier(f"=={version.major}.{version.minor}.*")
log.info("Reading in %r -> %s @ %s", str(identifier), spec, version)
orig_config = copy.copy(config)
config_update: AnyConfig | None = None
config_update: Config | None = None
# We need to use ** in update due to MyPy (probably a bug)
if "macosx" in identifier:
if identifier.startswith("cp"):
config_update = self.macos_cpython.update_version_macos(identifier, version, spec)
config_update = self.cpython.update_version_macos(identifier, version, spec)
elif identifier.startswith("pp"):
if "macosx_x86_64" in identifier:
config_update = self.macos_pypy.update_version_macos(spec)
elif "macosx_arm64" in identifier:
config_update = self.macos_pypy_arm64.update_version_macos(spec)
elif identifier.startswith("gp"):
if "macosx_x86_64" in identifier:
return
config_update = self.graalpy.update_version(identifier, spec)
elif "t-win32" in identifier and identifier.startswith("cp"):
config_update = self.windows_t_32.update_version_windows(spec)
elif "win32" in identifier and identifier.startswith("cp"):
@@ -278,16 +463,29 @@ class AllVersions:
config_update = self.windows_64.update_version_windows(spec)
elif identifier.startswith("pp"):
config_update = self.windows_pypy_64.update_version_windows(spec)
elif identifier.startswith("gp"):
config_update = self.graalpy.update_version(identifier, spec)
elif "t-win_arm64" in identifier and identifier.startswith("cp"):
config_update = self.windows_t_arm64.update_version_windows(spec)
elif "win_arm64" in identifier and identifier.startswith("cp"):
config_update = self.windows_arm64.update_version_windows(spec)
elif "android" in identifier:
# Python 3.13 is released by Chaquopy on Maven Central.
# Python 3.14 and newer have official releases on python.org.
versions = self.maven if identifier.startswith("cp313") else self.cpython
config_update = versions.update_version_android(identifier, spec)
elif "ios" in identifier:
config_update = self.ios_cpython.update_version_ios(identifier, version)
elif "pyodide" in identifier:
config_update = self.pyodide.update_version_pyodide(
identifier, version, spec, config["node_version"]
)
assert config_update is not None, f"{identifier} not found!"
config.update(**config_update)
if config != orig_config:
log.info(" Updated %s to %s", orig_config, config)
if config_update != config:
log.info(" Updated %s to %s", config, config_update)
config.clear()
config.update(**config_update)
@click.command()
@@ -311,11 +509,9 @@ def update_pythons(force: bool, level: str) -> None:
with toml_file_path.open("rb") as f:
configs = tomllib.load(f)
for config in configs["windows"]["python_configurations"]:
all_versions.update_config(config)
for config in configs["macos"]["python_configurations"]:
all_versions.update_config(config)
for platform in ["windows", "macos", "android", "ios", "pyodide"]:
for config in configs[platform]["python_configurations"]:
all_versions.update_config(config)
result_toml = dump_python_configurations(configs)
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent / ".."
CHANGELOG_FILE = PROJECT_ROOT / "docs" / "changelog.md"
README_FILE = PROJECT_ROOT / "README.md"
# https://regexr.com/622ds
FIRST_5_CHANGELOG_ENTRIES_REGEX = re.compile(r"""(^###.*?(?=###)){5}""", re.DOTALL | re.MULTILINE)
# https://regexr.com/622e5
README_CHANGELOG_SECTION = re.compile(
r"""(?<=<!-- START bin\/update_readme_changelog.py -->\n).*(?=<!-- END bin\/update_readme_changelog.py -->)""",
re.DOTALL,
)
def main():
changelog_text = CHANGELOG_FILE.read_text()
readme_text = README_FILE.read_text()
mini_changelog_match = FIRST_5_CHANGELOG_ENTRIES_REGEX.search(changelog_text)
assert mini_changelog_match, "Failed to find the first few changelog entries"
mini_changelog = "\n".join(
[
"",
"<!-- this section was generated by bin/update_readme_changelog.py -- do not edit manually -->",
"",
mini_changelog_match.group(0).strip(),
"",
"",
]
)
if not re.search(README_CHANGELOG_SECTION, readme_text):
sys.exit("Changelog section not found in README")
readme_text = re.sub(README_CHANGELOG_SECTION, mini_changelog, readme_text)
README_FILE.write_text(readme_text)
if __name__ == "__main__":
main()
+33 -34
View File
@@ -1,21 +1,20 @@
#!/usr/bin/env python3
from __future__ import annotations
import dataclasses
import difflib
import logging
import subprocess
from dataclasses import dataclass
import tomllib
from pathlib import Path
from typing import Final
import click
import rich
from packaging.version import InvalidVersion, Version
from packaging.version import Version
from rich.logging import RichHandler
from rich.syntax import Syntax
from cibuildwheel._compat import tomllib
from cibuildwheel.extra import github_api_request
log = logging.getLogger("cibw")
@@ -30,34 +29,28 @@ GET_VIRTUALENV_URL_TEMPLATE: Final[str] = (
)
@dataclass(frozen=True, order=True)
@dataclasses.dataclass(frozen=True, order=True)
class VersionTuple:
name: str
download_url: str
version: Version
version_string: str
def git_ls_remote_versions(url) -> list[VersionTuple]:
versions: list[VersionTuple] = []
tags = subprocess.run(
["git", "ls-remote", "--tags", url], check=True, text=True, capture_output=True
).stdout.splitlines()
for tag in tags:
_, ref = tag.split()
assert ref.startswith("refs/tags/")
version_string = ref[10:]
try:
version = Version(version_string)
if version.is_devrelease:
log.info("Ignoring development release %r", str(version))
continue
if version.is_prerelease:
log.info("Ignoring pre-release %r", str(version))
continue
versions.append(VersionTuple(version, version_string))
except InvalidVersion:
log.warning("Ignoring ref %r", ref)
versions.sort(reverse=True)
return versions
def get_latest_virtualenv_release() -> VersionTuple:
response = github_api_request("repos/pypa/get-virtualenv/releases/latest")
tag_name = response["tag_name"]
asset = next(
(asset for asset in response["assets"] if asset["name"] == "virtualenv.pyz"),
None,
)
if not asset:
msg = "No asset named 'virtualenv.pyz' found in the latest release of get-virtualenv."
raise RuntimeError(msg)
return VersionTuple(
version=Version(tag_name), name=tag_name, download_url=asset["browser_download_url"]
)
@click.command()
@@ -80,14 +73,20 @@ def update_virtualenv(force: bool, level: str) -> None:
with toml_file_path.open("rb") as f:
configurations = tomllib.load(f)
default = configurations.pop("default")
version = str(default["version"])
versions = git_ls_remote_versions(GET_VIRTUALENV_GITHUB)
if versions[0].version > Version(version):
version = versions[0].version_string
local_version = str(default["version"])
latest_release = get_latest_virtualenv_release()
if latest_release.version > Version(local_version):
version = latest_release.name
url = latest_release.download_url
else:
version = local_version
url = default["url"]
configurations["default"] = {
"version": version,
"url": GET_VIRTUALENV_URL_TEMPLATE.format(version=version),
"url": url,
}
result_toml = "".join(
f'{key} = {{ version = "{value["version"]}", url = "{value["url"]}" }}\n'
+1 -3
View File
@@ -1,3 +1 @@
from __future__ import annotations
__version__ = "2.18.1"
__version__ = "3.4.1"
+301 -124
View File
@@ -1,57 +1,109 @@
from __future__ import annotations
import argparse
import contextlib
import dataclasses
import functools
import os
import shutil
import sys
import tarfile
import textwrap
import traceback
import typing
from collections.abc import Iterable, Sequence, Set
from collections.abc import Generator, Iterable, Sequence
from pathlib import Path
from tempfile import mkdtemp
from typing import Protocol
from typing import Any, Literal, TextIO
import cibuildwheel
import cibuildwheel.linux
import cibuildwheel.macos
import cibuildwheel.util
import cibuildwheel.windows
from cibuildwheel._compat.typing import assert_never
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.ci import CIProvider, detect_ci_provider, fix_ansi_codes_for_github_actions
from cibuildwheel.logger import log
from cibuildwheel.options import CommandLineArguments, Options, compute_options
from cibuildwheel.typing import PLATFORMS, GenericPythonConfiguration, PlatformName
from cibuildwheel.util import (
CIBW_CACHE_PATH,
BuildSelector,
CIProvider,
Unbuffered,
chdir,
detect_ci_provider,
fix_ansi_codes_for_github_actions,
)
from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers, native_platform
from cibuildwheel.selector import BuildSelector, EnableGroup, selector_matches
from cibuildwheel.typing import PLATFORMS, PlatformName
from cibuildwheel.util.file import CIBW_CACHE_PATH, ensure_cache_sentinel
from cibuildwheel.util.helpers import strtobool
from cibuildwheel.util.resources import read_all_configs
@dataclasses.dataclass
class GlobalOptions:
print_traceback_on_error: bool = True # decides what happens when errors are hit.
@dataclasses.dataclass(frozen=True)
class FileReport:
name: str
size: str
# Taken from https://stackoverflow.com/a/107717
class Unbuffered:
def __init__(self, stream: TextIO) -> None:
self.stream = stream
def write(self, data: str) -> None:
self.stream.write(data)
self.stream.flush()
def writelines(self, data: Iterable[str]) -> None:
self.stream.writelines(data)
self.stream.flush()
def __getattr__(self, attr: str) -> Any: # noqa: ANN401
return getattr(self.stream, attr)
def main() -> None:
parser = argparse.ArgumentParser(
global_options = GlobalOptions()
try:
main_inner(global_options)
except errors.FatalError as e:
message = e.args[0]
if log.step_active:
log.step_end_with_error(message)
else:
log.error(message)
if global_options.print_traceback_on_error:
traceback.print_exc(file=sys.stderr)
sys.exit(e.return_code)
def main_inner(global_options: GlobalOptions) -> None:
"""
`main_inner` is the same as `main`, but it raises FatalError exceptions
rather than exiting directly.
"""
# Default in 3.15+, only needed on 3.14
if sys.version_info >= (3, 14):
arg_parser = functools.partial(argparse.ArgumentParser, suggest_on_error=True)
else:
arg_parser = argparse.ArgumentParser
parser = arg_parser(
description="Build wheels for all the platforms.",
epilog="""
Most options are supplied via environment variables or in
--config-file (pyproject.toml usually). See
https://github.com/pypa/cibuildwheel#options for info.
""",
allow_abbrev=False,
)
parser.add_argument(
"--platform",
choices=["auto", "linux", "macos", "windows"],
choices=["auto", "linux", "macos", "windows", "pyodide", "android", "ios"],
default=None,
help="""
Platform to build for. Use this option to override the
auto-detected platform. Specifying "macos" or "windows" only works
on that operating system, but "linux" works on all three, as long
as Docker/Podman is installed. Default: auto.
Platform to build for. Use this option to override the auto-detected
platform. Specifying "macos" or "windows" only works on that
operating system. "linux" works on any desktop OS, as long as
Docker/Podman is installed. "pyodide" and "android" only work on
Linux and macOS. "ios" only works on macOS. Default: auto.
""",
)
@@ -69,9 +121,22 @@ def main() -> None:
""",
)
enable_groups_str = ", ".join(g.value for g in EnableGroup)
parser.add_argument(
"--enable",
action="append",
default=[],
metavar="GROUP",
help=f"""
Enable an additional category of builds. Use multiple times to select multiple groups. Choices: {enable_groups_str}.
""",
)
parser.add_argument(
"--only",
default=None,
choices=[v["identifier"] for vv in read_all_configs().values() for v in vv],
metavar="IDENTIFIER",
help="""
Force a single wheel build when given an identifier. Overrides
CIBW_BUILD/CIBW_SKIP. --platform and --arch cannot be specified
@@ -99,7 +164,7 @@ def main() -> None:
parser.add_argument(
"package_dir",
metavar="PACKAGE",
default=Path("."),
default=Path(),
type=Path,
nargs="?",
help="""
@@ -119,6 +184,12 @@ def main() -> None:
help="Print the build identifiers matched by the current invocation and exit.",
)
parser.add_argument(
"--clean-cache",
action="store_true",
help="Clear the cibuildwheel cache and exit.",
)
parser.add_argument(
"--allow-empty",
action="store_true",
@@ -126,13 +197,58 @@ def main() -> None:
)
parser.add_argument(
"--prerelease-pythons",
"--debug-traceback",
action="store_true",
help="Enable pre-release Python versions if available.",
default=strtobool(os.environ.get("CIBW_DEBUG_TRACEBACK", "0")),
help="Print a full traceback for all errors",
)
args = CommandLineArguments(**vars(parser.parse_args()))
global_options.print_traceback_on_error = args.debug_traceback
if args.clean_cache:
if not CIBW_CACHE_PATH.exists():
print(f"Cache directory does not exist: {CIBW_CACHE_PATH}")
sys.exit(0)
sentinel_file = CIBW_CACHE_PATH / "CACHEDIR.TAG"
if not sentinel_file.exists():
print(
f"Error: {CIBW_CACHE_PATH} does not appear to be a cibuildwheel cache directory.",
"Only directories with a CACHEDIR.TAG sentinel file can be cleaned.",
sep="\n",
file=sys.stderr,
)
sys.exit(1)
# Verify signature to ensure it's a proper cache dir
# See https://bford.info/cachedir/ for more
try:
sentinel_content = sentinel_file.read_text(encoding="utf-8")
except OSError as e:
print(f"Error reading cache directory tag: {e}", file=sys.stderr)
sys.exit(1)
if not sentinel_content.startswith("Signature: 8a477f597d28d172789f06886806bc55"):
print(
f"Error: {sentinel_file} does not contain a valid cache directory signature.",
"For safety, only properly signed cache directories can be cleaned.",
sep="\n",
file=sys.stderr,
)
sys.exit(1)
print(f"Clearing cache directory: {CIBW_CACHE_PATH}")
try:
shutil.rmtree(CIBW_CACHE_PATH)
print("Cache cleared successfully.")
except OSError as e:
print(f"Error clearing cache: {e}", file=sys.stderr)
sys.exit(1)
sys.exit(0)
args.package_dir = args.package_dir.resolve()
# This are always relative to the base directory, even in SDist builds
@@ -159,7 +275,7 @@ def main() -> None:
# This is now the new package dir
args.package_dir = project_dir.resolve()
with chdir(project_dir):
with contextlib.chdir(project_dir):
build_in_directory(args)
finally:
# avoid https://github.com/python/cpython/issues/86962 by performing
@@ -176,81 +292,44 @@ def _compute_platform_only(only: str) -> PlatformName:
return "macos"
if "win_" in only or "win32" in only:
return "windows"
print(
f"Invalid --only='{only}', must be a build selector with a known platform",
file=sys.stderr,
)
sys.exit(2)
def _compute_platform_auto() -> PlatformName:
if sys.platform.startswith("linux"):
return "linux"
elif sys.platform == "darwin":
return "macos"
elif sys.platform == "win32":
return "windows"
else:
print(
'cibuildwheel: Unable to detect platform from "sys.platform". cibuildwheel doesn\'t '
"support building wheels for this platform. You might be able to build for a different "
"platform using the --platform argument. Check --help output for more information.",
file=sys.stderr,
)
sys.exit(2)
if "pyodide_" in only:
return "pyodide"
if "android_" in only:
return "android"
if "ios_" in only:
return "ios"
msg = f"Invalid --only='{only}', must be a build selector with a known platform"
raise errors.ConfigurationError(msg)
def _compute_platform(args: CommandLineArguments) -> PlatformName:
platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "auto")
platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "") or "auto"
if args.only and args.platform is not None:
print(
"--platform cannot be specified with --only, it is computed from --only",
file=sys.stderr,
)
sys.exit(2)
msg = "--platform cannot be specified with --only, it is computed from --only"
raise errors.ConfigurationError(msg)
if args.only and args.archs is not None:
print(
"--arch cannot be specified with --only, it is computed from --only",
file=sys.stderr,
)
sys.exit(2)
msg = "--arch cannot be specified with --only, it is computed from --only"
raise errors.ConfigurationError(msg)
if platform_option_value not in PLATFORMS | {"auto"}:
print(f"cibuildwheel: Unsupported platform: {platform_option_value}", file=sys.stderr)
sys.exit(2)
msg = f"Unsupported platform: {platform_option_value}"
raise errors.ConfigurationError(msg)
if args.only:
return _compute_platform_only(args.only)
elif platform_option_value != "auto":
return typing.cast(PlatformName, platform_option_value)
return typing.cast("PlatformName", platform_option_value)
return _compute_platform_auto()
class PlatformModule(Protocol):
# note that as per PEP544, the self argument is ignored when the protocol
# is applied to a module
def get_python_configurations(
self, build_selector: BuildSelector, architectures: Set[Architecture]
) -> Sequence[GenericPythonConfiguration]: ...
def build(self, options: Options, tmp_path: Path) -> None: ...
# pylint: disable-next=inconsistent-return-statements
def get_platform_module(platform: PlatformName) -> PlatformModule:
if platform == "linux":
return cibuildwheel.linux
if platform == "windows":
return cibuildwheel.windows
if platform == "macos":
return cibuildwheel.macos
assert_never(platform)
return native_platform()
def build_in_directory(args: CommandLineArguments) -> None:
platform: PlatformName = _compute_platform(args)
if platform == "pyodide" and sys.platform == "win32":
msg = "Building for pyodide is not supported on Windows"
raise errors.ConfigurationError(msg)
options = compute_options(platform=platform, command_line_arguments=args, env=os.environ)
package_dir = options.globals.package_dir
@@ -258,11 +337,10 @@ def build_in_directory(args: CommandLineArguments) -> None:
if not any(package_dir.joinpath(name).exists() for name in package_files):
names = ", ".join(sorted(package_files, reverse=True))
msg = f"cibuildwheel: Could not find any of {{{names}}} at root of package"
print(msg, file=sys.stderr)
sys.exit(2)
msg = f"Could not find any of {{{names}}} at root of package"
raise errors.ConfigurationError(msg)
platform_module = get_platform_module(platform)
platform_module = ALL_PLATFORM_MODULES[platform]
identifiers = get_build_identifiers(
platform_module=platform_module,
build_selector=options.globals.build_selector,
@@ -280,10 +358,11 @@ def build_in_directory(args: CommandLineArguments) -> None:
# Python is buffering by default when running on the CI platforms, giving
# problems interleaving subprocess call output with unflushed calls to
# 'print'
sys.stdout = Unbuffered(sys.stdout) # type: ignore[assignment]
sys.stdout = Unbuffered(sys.stdout)
# create the cache dir before it gets printed & builds performed
CIBW_CACHE_PATH.mkdir(parents=True, exist_ok=True)
ensure_cache_sentinel(CIBW_CACHE_PATH)
print_preamble(platform=platform, options=options, identifiers=identifiers)
@@ -291,27 +370,22 @@ def build_in_directory(args: CommandLineArguments) -> None:
options.check_for_invalid_configuration(identifiers)
allowed_architectures_check(platform, options.globals.architectures)
except ValueError as err:
print("cibuildwheel:", *err.args, file=sys.stderr)
sys.exit(4)
raise errors.DeprecationError(*err.args) from err
if not identifiers:
print(
f"cibuildwheel: No build identifiers selected: {options.globals.build_selector}",
file=sys.stderr,
)
if not args.allow_empty:
sys.exit(3)
message = f"No build identifiers selected: {options.globals.build_selector}"
if options.globals.allow_empty:
print(f"cibuildwheel: {message}", file=sys.stderr)
else:
raise errors.NothingToDoError(message)
output_dir = options.globals.output_dir
if not output_dir.exists():
output_dir.mkdir(parents=True)
output_dir.mkdir(parents=True, exist_ok=True)
tmp_path = Path(mkdtemp(prefix="cibw-run-")).resolve(strict=True)
try:
with cibuildwheel.util.print_new_wheels(
"\n{n} wheels produced in {m:.0f} minutes:", output_dir
):
with log.print_summary(options=options):
platform_module.build(options, tmp_path)
finally:
# avoid https://github.com/python/cpython/issues/86962 by performing
@@ -344,36 +418,139 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str])
print()
print(f"Cache folder: {CIBW_CACHE_PATH}")
print()
warnings = detect_warnings(options=options, identifiers=identifiers)
if warnings:
print("\nWarnings:")
for warning in warnings:
print(" " + warning)
warnings = detect_warnings(options=options)
for warning in warnings:
log.warning(warning)
print("\nHere we go!\n")
error_list = list(detect_errors(options=options, identifiers=identifiers))
if error_list:
for error in error_list:
log.error(error)
msg = "\n".join(error_list)
raise errors.ConfigurationError(msg)
n = len(identifiers)
print(f"{n} build{'s' if n != 1 else ''} selected:")
print(", ".join(identifiers))
print()
print("Here we go!\n")
def get_build_identifiers(
platform_module: PlatformModule, build_selector: BuildSelector, architectures: Set[Architecture]
) -> list[str]:
python_configurations = platform_module.get_python_configurations(build_selector, architectures)
return [config.identifier for config in python_configurations]
def detect_errors(*, options: Options, identifiers: Iterable[str]) -> Generator[str, None, None]:
# Check for deprecated CIBW_FREE_THREADED_SUPPORT environment variable
if "CIBW_FREE_THREADED_SUPPORT" in os.environ:
yield "CIBW_FREE_THREADED_SUPPORT environment variable is no longer supported."
def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]:
warnings = []
# warn about deprecated {python} and {pip}
# Deprecated {python} and {pip}
for option_name in ["test_command", "before_build"]:
option_values = [getattr(options.build_options(i), option_name) for i in identifiers]
if any(o and ("{python}" in o or "{pip}" in o) for o in option_values):
# Reminder: in an f-string, double braces means literal single brace
msg = (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
"and will be removed in a future release. Simply use 'python' or 'pip' instead."
yield (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer supported "
"and have been removed in cibuildwheel 3. Simply use 'python' or 'pip' instead."
)
def detect_warnings(*, options: Options) -> Generator[str, None, None]:
python_version_deprecation = ((3, 11), 3)
if sys.version_info[:2] < python_version_deprecation[0]:
python_version = ".".join(map(str, python_version_deprecation[0]))
yield (
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"
)
build_selector = options.globals.build_selector
test_selector = options.globals.test_selector
if EnableGroup.CPythonFreeThreading in build_selector.enable:
yield (
"'cpython-freethreading' enable is deprecated and will be removed in a future version. "
"It should be removed from tool.cibuildwheel.enable in pyproject.toml "
"or CIBW_ENABLE environment variable."
)
all_valid_identifiers = [
config.identifier
for module in ALL_PLATFORM_MODULES.values()
for config in module.all_python_configurations()
]
enabled_selector = BuildSelector(
build_config="*", skip_config="", enable=options.globals.build_selector.enable
)
all_enabled_identifiers = [
identifier for identifier in all_valid_identifiers if enabled_selector(identifier)
]
yield from check_for_invalid_selectors(
selector_name="build",
selector_value=build_selector.build_config,
all_valid_identifiers=all_valid_identifiers,
all_enabled_identifiers=all_enabled_identifiers,
)
yield from check_for_invalid_selectors(
selector_name="skip",
selector_value=build_selector.skip_config,
all_valid_identifiers=all_valid_identifiers,
all_enabled_identifiers=all_enabled_identifiers,
)
yield from check_for_invalid_selectors(
selector_name="test_skip",
selector_value=test_selector.skip_config,
all_valid_identifiers=all_valid_identifiers,
all_enabled_identifiers=all_enabled_identifiers,
)
def check_for_invalid_selectors(
*,
selector_name: Literal["build", "skip", "test_skip"],
selector_value: str,
all_valid_identifiers: Sequence[str],
all_enabled_identifiers: Sequence[str],
) -> list[str]:
warnings = []
for selector in selector_value.split():
selector_ = selector
if selector_name == "test_skip":
# macosx_universal2 uses an additional identifier for tests which ends with ":{arch}"
values = selector.split(":")
universal2_identifiers = filter(
lambda x: x.endswith("-macosx_universal2"), all_valid_identifiers
)
if len(values) == 2 and any(
selector_matches(selector_, f"{i}:{arch}")
for i in universal2_identifiers
for arch in ["arm64", "x86_64"]
):
# just ignore the arch part in the rest of the check
selector_ = values[0]
if not any(selector_matches(selector_, i) for i in all_enabled_identifiers):
msg = f"Invalid {selector_name} selector: {selector!r}. "
error_type: type = errors.ConfigurationError
if any(selector_matches(selector_, i) for i in all_valid_identifiers):
msg += "This selector matches a group that wasn't enabled. Enable it using the `enable` option or remove this selector. "
if "p2" in selector_ or "p35" in selector_:
msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update `{selector_name}`. "
error_type = errors.DeprecationError
if "p36" in selector_ or "p37" in selector_:
msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update `{selector_name}`. "
error_type = errors.DeprecationError
if selector_name == "build":
raise error_type(msg)
msg += "This selector will have no effect. "
warnings.append(msg)
return warnings
-1
View File
@@ -1 +0,0 @@
from __future__ import annotations
-10
View File
@@ -1,10 +0,0 @@
from __future__ import annotations
import sys
if sys.version_info >= (3, 11):
from tomllib import load, loads
else:
from tomli import load, loads
__all__ = ["load", "loads"]
-14
View File
@@ -1,14 +0,0 @@
from __future__ import annotations
import sys
if sys.version_info < (3, 11):
from typing_extensions import NotRequired, Self, assert_never
else:
from typing import NotRequired, Self, assert_never
__all__ = (
"assert_never",
"NotRequired",
"Self",
)
+156 -77
View File
@@ -1,79 +1,115 @@
from __future__ import annotations
import functools
import platform as platform_module
import re
import shutil
import subprocess
import sys
import typing
from collections.abc import Set
from enum import Enum
from typing import Final, Literal
from enum import StrEnum, auto
from typing import Final, Literal, Self
from ._compat.typing import assert_never
from .typing import PlatformName
from cibuildwheel import errors
from cibuildwheel.typing import PlatformName
PRETTY_NAMES: Final[dict[PlatformName, str]] = {
"linux": "Linux",
"macos": "macOS",
"windows": "Windows",
"pyodide": "Pyodide",
"android": "Android",
"ios": "iOS",
}
ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
{"linux": "x86_64", "macos": "x86_64", "windows": "AMD64"},
{"linux": "x86_64", "macos": "x86_64", "windows": "AMD64", "android": "x86_64"},
{"linux": "i686", "macos": None, "windows": "x86"},
{"linux": "aarch64", "macos": "arm64", "windows": "ARM64"},
{"linux": "aarch64", "macos": "arm64", "windows": "ARM64", "android": "arm64_v8a"},
]
@functools.total_ordering
class Architecture(Enum):
value: str
def arch_synonym(arch: str, from_platform: PlatformName, to_platform: PlatformName) -> str | None:
for arch_synonym_ in ARCH_SYNONYMS:
if arch == arch_synonym_.get(from_platform):
return arch_synonym_.get(to_platform, arch)
# mac/linux archs
x86_64 = "x86_64"
return arch
def _check_aarch32_el0() -> bool:
"""Check if running armv7l natively on aarch64 is supported"""
if not sys.platform.startswith("linux"):
return False
if platform_module.machine() != "aarch64":
return False
executable = shutil.which("linux32")
if executable is None:
return False
check = subprocess.run([executable, "uname", "-m"], check=False, capture_output=True, text=True)
return check.returncode == 0 and check.stdout.startswith("armv")
@typing.final
class Architecture(StrEnum):
# mac/linux/android archs
x86_64 = auto()
# linux archs
i686 = "i686"
aarch64 = "aarch64"
ppc64le = "ppc64le"
s390x = "s390x"
i686 = auto()
aarch64 = auto()
ppc64le = auto()
s390x = auto()
armv7l = auto()
riscv64 = auto()
# mac archs
universal2 = "universal2"
arm64 = "arm64"
universal2 = auto()
arm64 = auto()
# windows archs
x86 = "x86"
x86 = auto()
AMD64 = "AMD64"
ARM64 = "ARM64"
# Allow this to be sorted
def __lt__(self, other: Architecture) -> bool:
return self.value < other.value
# WebAssembly
wasm32 = auto()
def __str__(self) -> str:
return self.name
# android archs
arm64_v8a = auto()
@staticmethod
def parse_config(config: str, platform: PlatformName) -> set[Architecture]:
# iOS "multiarch" architectures that include both
# the CPU architecture and the ABI.
arm64_iphoneos = auto()
arm64_iphonesimulator = auto()
x86_64_iphonesimulator = auto()
@classmethod
def parse_config(cls, config: str, platform: PlatformName) -> set[Self]:
result = set()
for arch_str in re.split(r"[\s,]+", config):
if arch_str == "auto":
result |= Architecture.auto_archs(platform=platform)
elif arch_str == "native":
result.add(Architecture(platform_module.machine()))
elif arch_str == "all":
result |= Architecture.all_archs(platform=platform)
elif arch_str == "auto64":
result |= Architecture.bitness_archs(platform=platform, bitness="64")
elif arch_str == "auto32":
result |= Architecture.bitness_archs(platform=platform, bitness="32")
else:
result.add(Architecture(arch_str))
match arch_str:
case "auto":
result |= cls.auto_archs(platform=platform)
case "native":
if native_arch := cls.native_arch(platform=platform):
result.add(native_arch)
case "all":
result |= cls.all_archs(platform=platform)
case "auto64":
result |= cls.bitness_archs(platform=platform, bitness="64")
case "auto32":
result |= cls.bitness_archs(platform=platform, bitness="32")
case _:
try:
result.add(cls(arch_str))
except ValueError as e:
msg = f"Invalid architecture '{arch_str}'"
raise errors.ConfigurationError(msg) from e
return result
@staticmethod
def auto_archs(platform: PlatformName) -> set[Architecture]:
@classmethod
def native_arch(cls, platform: PlatformName) -> Self | None:
native_machine = platform_module.machine()
native_architecture = cls(native_machine)
# Cross-platform support. Used for --print-build-identifiers or docker builds.
host_platform: PlatformName = (
@@ -82,59 +118,102 @@ class Architecture(Enum):
else ("macos" if sys.platform.startswith("darwin") else "linux")
)
native_architecture = Architecture(native_machine)
if platform == "pyodide":
return cls.wasm32
elif platform == "ios":
# Can only build for iOS on macOS. The "native" architecture is the
# simulator for the macOS native platform.
if host_platform == "macos":
if native_architecture == cls.x86_64:
return cls.x86_64_iphonesimulator
else:
return cls.arm64_iphonesimulator
else:
return None
# we might need to rename the native arch to the machine we're running
# on, as the same arch can have different names on different platforms
if host_platform != platform:
for arch_synonym in ARCH_SYNONYMS:
if native_machine == arch_synonym.get(host_platform):
synonym = arch_synonym[platform]
synonym = arch_synonym(native_machine, host_platform, platform)
if synonym is None:
# can't build anything on this platform
return None
if synonym is None:
# can't build anything on this platform
return set()
native_architecture = cls(synonym)
native_architecture = Architecture(synonym)
return native_architecture
result = {native_architecture}
@classmethod
def auto_archs(cls, platform: PlatformName) -> set[Self]:
native_arch = cls.native_arch(platform)
if native_arch is None:
return set() # can't build anything on this platform
result = {native_arch}
if platform == "linux" and Architecture.x86_64 in result:
# x86_64 machines can run i686 containers
result.add(Architecture.i686)
if platform == "windows" and Architecture.AMD64 in result:
result.add(Architecture.x86)
match platform:
case "windows" if cls.AMD64 in result:
result.add(cls.x86)
case "ios" if native_arch == cls.arm64_iphonesimulator:
# Also build the device wheel if we're on ARM64.
result.add(cls.arm64_iphoneos)
return result
@staticmethod
def all_archs(platform: PlatformName) -> set[Architecture]:
@classmethod
def all_archs(cls, platform: PlatformName) -> set[Self]:
all_archs_map = {
"linux": {
Architecture.x86_64,
Architecture.i686,
Architecture.aarch64,
Architecture.ppc64le,
Architecture.s390x,
cls.x86_64,
cls.i686,
cls.aarch64,
cls.ppc64le,
cls.s390x,
cls.armv7l,
cls.riscv64,
},
"macos": {cls.x86_64, cls.arm64, cls.universal2},
"windows": {cls.x86, cls.AMD64, cls.ARM64},
"pyodide": {cls.wasm32},
"android": {cls.x86_64, cls.arm64_v8a},
"ios": {
cls.x86_64_iphonesimulator,
cls.arm64_iphonesimulator,
cls.arm64_iphoneos,
},
"macos": {Architecture.x86_64, Architecture.arm64, Architecture.universal2},
"windows": {Architecture.x86, Architecture.AMD64, Architecture.ARM64},
}
return all_archs_map[platform]
@staticmethod
# pylint: disable-next=inconsistent-return-statements
def bitness_archs(platform: PlatformName, bitness: Literal["64", "32"]) -> set[Architecture]:
archs_32 = {Architecture.i686, Architecture.x86}
auto_archs = Architecture.auto_archs(platform)
@classmethod
def bitness_archs(cls, platform: PlatformName, bitness: Literal["64", "32"]) -> set[Self]:
# This map maps 64-bit architectures to their 32-bit equivalents.
archs_map = {
cls.x86_64: cls.i686,
cls.AMD64: cls.x86,
cls.aarch64: cls.armv7l,
}
native_arch = cls.native_arch(platform)
if bitness == "64":
return auto_archs - archs_32
elif bitness == "32":
return auto_archs & archs_32
else:
assert_never(bitness)
if native_arch is None:
return set() # can't build anything on this platform
if native_arch == cls.wasm32:
return {native_arch} if bitness == "32" else set()
match bitness:
case "64":
return {native_arch} if native_arch not in archs_map.values() else set()
case "32":
if native_arch in archs_map.values():
return {native_arch}
elif native_arch in archs_map and platform in {"linux", "windows"}:
if native_arch == cls.aarch64 and not _check_aarch32_el0():
# If we're on aarch64, skip if we cannot build armv7l wheels.
return set()
return {archs_map[native_arch]}
else:
return set()
case _:
typing.assert_never(bitness)
def allowed_architectures_check(
+9 -7
View File
@@ -1,21 +1,23 @@
from __future__ import annotations
import dataclasses
import subprocess
from collections.abc import Iterable, Mapping, Sequence
from dataclasses import dataclass
from typing import Callable, Dict, List # noqa: TID251
from collections.abc import (
Callable,
Iterable,
Mapping,
Sequence,
)
import bashlex
# a function that takes a command and the environment, and returns the result
EnvironmentExecutor = Callable[[List[str], Dict[str, str]], str]
EnvironmentExecutor = Callable[[list[str], dict[str, str]], str]
def local_environment_executor(command: Sequence[str], env: Mapping[str, str]) -> str:
return subprocess.run(command, env=env, text=True, stdout=subprocess.PIPE, check=True).stdout
@dataclass(frozen=True)
@dataclasses.dataclass(frozen=True, kw_only=True)
class NodeExecutionContext:
environment: dict[str, str]
input: str
+79
View File
@@ -0,0 +1,79 @@
import os
import re
from enum import Enum
from cibuildwheel.util.helpers import strtobool
ANSI_CODE_REGEX = re.compile(r"(\033\[[0-9;]*m)")
class CIProvider(Enum):
# official support
travis_ci = "travis"
circle_ci = "circle_ci"
azure_pipelines = "azure_pipelines"
github_actions = "github_actions"
gitlab = "gitlab"
cirrus_ci = "cirrus_ci"
# unofficial support
appveyor = "appveyor"
other = "other"
def detect_ci_provider() -> CIProvider | None:
if "TRAVIS" in os.environ:
return CIProvider.travis_ci
elif "APPVEYOR" in os.environ:
return CIProvider.appveyor
elif "CIRCLECI" in os.environ:
return CIProvider.circle_ci
elif "AZURE_HTTP_USER_AGENT" in os.environ:
return CIProvider.azure_pipelines
elif "GITHUB_ACTIONS" in os.environ:
return CIProvider.github_actions
elif "GITLAB_CI" in os.environ:
return CIProvider.gitlab
elif "CIRRUS_CI" in os.environ:
return CIProvider.cirrus_ci
elif strtobool(os.environ.get("CI", "false")):
return CIProvider.other
else:
return None
def fix_ansi_codes_for_github_actions(text: str) -> str:
"""
Github Actions forgets the current ANSI style on every new line. This
function repeats the current ANSI style on every new line.
"""
ansi_codes: list[str] = []
output = ""
for line in text.splitlines(keepends=True):
# add the current ANSI codes to the beginning of the line
output += "".join(ansi_codes) + line
# split the line at each ANSI code
parts = ANSI_CODE_REGEX.split(line)
# if there are any ANSI codes, save them
if len(parts) > 1:
# iterate over the ANSI codes in this line
for code in parts[1::2]:
if code == "\033[0m":
# reset the list of ANSI codes when the clear code is found
ansi_codes = []
else:
ansi_codes.append(code)
return output
def filter_ansi_codes(text: str, /) -> str:
"""
Remove ANSI codes from text.
"""
return ANSI_CODE_REGEX.sub("", text)
+7 -10
View File
@@ -1,5 +1,3 @@
from __future__ import annotations
import dataclasses
from collections.abc import Mapping, Sequence
from typing import Any, Protocol
@@ -7,7 +5,7 @@ from typing import Any, Protocol
import bashlex
import bashlex.errors
from . import bashlex_eval
from cibuildwheel import bashlex_eval
class EnvironmentParseError(Exception):
@@ -75,12 +73,16 @@ class EnvironmentAssignmentRaw:
return self.value
@dataclasses.dataclass
class EnvironmentAssignmentBash:
"""
An environment variable, in bash syntax. The value can use bash constructs
like "$OTHER_VAR" and "$(command arg1 arg2)".
"""
name: str
value: str
def __init__(self, assignment: str):
name, equals, value = assignment.partition("=")
if not equals:
@@ -98,13 +100,8 @@ class EnvironmentAssignmentBash:
def __repr__(self) -> str:
return f"{self.name}={self.value}"
def __eq__(self, other: object) -> bool:
if isinstance(other, EnvironmentAssignmentBash):
return self.name == other.name and self.value == other.value
return False
@dataclasses.dataclass
@dataclasses.dataclass(kw_only=True)
class ParsedEnvironment:
assignments: list[EnvironmentAssignment]
@@ -134,7 +131,7 @@ class ParsedEnvironment:
def __repr__(self) -> str:
return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})"
def options_summary(self) -> Any:
def options_summary(self) -> list[EnvironmentAssignment]:
return self.assignments
+105
View File
@@ -0,0 +1,105 @@
"""
Errors that can cause the build to fail. Each subclass of FatalError has
a different return code, by defining them all here, we can ensure that they're
semantically clear and unique.
"""
import textwrap
class FatalError(BaseException):
"""
Raising an error of this type will cause the message to be printed to
stderr and the process to be terminated. Within cibuildwheel, raising this
exception produces a better error message, and optional traceback.
"""
return_code: int = 1
class ConfigurationError(FatalError):
return_code = 2
class NothingToDoError(FatalError):
return_code = 3
class DeprecationError(FatalError):
return_code = 4
class NonPlatformWheelError(FatalError):
def __init__(self) -> None:
message = textwrap.dedent(
"""
Build failed because a pure Python wheel was generated.
If you intend to build a pure-Python wheel, you don't need
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 cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
"""
)
super().__init__(message)
self.return_code = 5
class AlreadyBuiltWheelError(FatalError):
def __init__(self, wheel_name: str) -> None:
message = textwrap.dedent(
f"""
Build failed because a wheel named {wheel_name} was already generated in the current run.
If you expected another wheel to be generated, check your project configuration, or run
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
"""
)
super().__init__(message)
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
class RepairStepProducedMultipleWheelsError(FatalError):
def __init__(self, wheels: list[str]) -> None:
message = textwrap.dedent(
f"""
Build failed because the repair step completed successfully but
produced multiple wheels: {wheels}
Your `repair-wheel-command` is expected to place one 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
+70 -3
View File
@@ -2,11 +2,16 @@
These are utilities for the `/bin` scripts, not for the `cibuildwheel` program.
"""
from __future__ import annotations
import json
import time
import typing
import urllib.error
import urllib.request
from collections.abc import Mapping, Sequence
from io import StringIO
from typing import Protocol
from typing import Any, NotRequired, Protocol
from cibuildwheel import __version__ as cibw_version
__all__ = ("Printable", "dump_python_configurations")
@@ -32,3 +37,65 @@ def dump_python_configurations(
output.write("\n")
# Strip the final newline, to avoid two blank lines at the end.
return output.getvalue()[:-1]
def _json_request(request: urllib.request.Request, timeout: int = 30) -> dict[str, Any]:
with urllib.request.urlopen(request, timeout=timeout) as response:
return typing.cast("dict[str, Any]", json.load(response))
def github_api_request(path: str, *, max_retries: int = 3) -> dict[str, Any]:
"""
Makes a GitHub API request to the given path and returns the JSON response.
"""
api_url = f"https://api.github.com/{path}"
headers = {
"Accept": "application/vnd.github.v3+json",
"User-Agent": f"cibuildwheel/{cibw_version}",
}
request = urllib.request.Request(api_url, headers=headers)
for retry_count in range(max_retries):
try:
return _json_request(request)
except (urllib.error.URLError, TimeoutError) as e:
# pylint: disable=E1101
if (
isinstance(e, urllib.error.HTTPError)
and (e.code in {403, 429})
and e.headers.get("x-ratelimit-remaining") == "0"
):
reset_time = int(e.headers.get("x-ratelimit-reset", 0))
wait_time = max(0, reset_time - int(e.headers.get("date", 0)))
print(f"Github rate limit exceeded. Waiting for {wait_time} seconds.")
time.sleep(wait_time)
else:
print(f"Retrying GitHub API request due to error: {e}")
if retry_count == max_retries - 1:
print(f"GitHub API request failed (Network error: {e}). Check network connection.")
raise e
# Should never be reached but to keep the type checker happy
msg = "Unexpected execution path in github_api_request"
raise RuntimeError(msg)
class PyodideXBuildEnvRelease(typing.TypedDict):
version: str
python_version: str
emscripten_version: str
min_pyodide_build_version: NotRequired[str]
max_pyodide_build_version: NotRequired[str]
class PyodideXBuildEnvInfo(typing.TypedDict):
releases: dict[str, PyodideXBuildEnvRelease]
def get_pyodide_xbuildenv_info() -> PyodideXBuildEnvInfo:
xbuildenv_info_url = (
"https://pyodide.github.io/pyodide/api/pyodide-cross-build-environments.json"
)
with urllib.request.urlopen(xbuildenv_info_url) as response:
return typing.cast("PyodideXBuildEnvInfo", json.loads(response.read().decode("utf-8")))
+84
View File
@@ -0,0 +1,84 @@
import dataclasses
import shlex
import typing
from collections.abc import Sequence
from typing import Literal, Self, get_args
from cibuildwheel.logger import log
from cibuildwheel.util.helpers import parse_key_value_string
BuildFrontendName = Literal["pip", "build", "build[uv]", "uv"]
@dataclasses.dataclass(frozen=True)
class BuildFrontendConfig:
name: BuildFrontendName
args: Sequence[str] = ()
@classmethod
def from_config_string(cls, config_string: str) -> Self:
config_dict = parse_key_value_string(config_string, ["name"], ["args"])
name = " ".join(config_dict["name"])
if name not in get_args(BuildFrontendName):
names = ", ".join(repr(n) for n in get_args(BuildFrontendName))
msg = f"Unrecognised build frontend {name!r}, must be one of {names}"
raise ValueError(msg)
name = typing.cast("BuildFrontendName", name)
args = config_dict.get("args") or []
return cls(name=name, args=args)
def options_summary(self) -> str | dict[str, str]:
if not self.args:
return self.name
else:
return {"name": self.name, "args": repr(self.args)}
def _get_verbosity_flags(level: int, frontend: BuildFrontendName, *, py38: bool) -> list[str]:
if level < 0:
if frontend.startswith("build") and py38:
msg = f"build_verbosity {level} is not supported for {frontend} frontend. Ignoring."
log.warning(msg)
return []
return ["-" + -level * "q"]
if level > 0:
if frontend == "pip":
return ["-" + level * "v"]
if level > 1:
return ["-" + (level - 1) * "v"]
return []
def _split_config_settings(config_settings: str) -> list[str]:
config_settings_list = shlex.split(config_settings)
return [f"-C{setting}" for setting in config_settings_list]
# Based on build.__main__.main.
def parse_config_settings(config_settings_str: str) -> dict[str, str | list[str]]:
config_settings: dict[str, str | list[str]] = {}
for arg in shlex.split(config_settings_str):
setting, _, value = arg.partition("=")
existing_value = config_settings.get(setting)
if existing_value is None:
config_settings[setting] = value
elif isinstance(existing_value, str):
config_settings[setting] = [existing_value, value]
else:
existing_value.append(value)
return config_settings
def get_build_frontend_extra_flags(
build_frontend: BuildFrontendConfig, verbosity_level: int, config_settings: str, *, py38: bool
) -> list[str]:
return [
*_split_config_settings(config_settings),
*build_frontend.args,
*_get_verbosity_flags(verbosity_level, build_frontend.name, py38=py38),
]
+209 -58
View File
@@ -1,15 +1,26 @@
from __future__ import annotations
import codecs
import contextlib
import dataclasses
import functools
import hashlib
import io
import os
import re
import sys
import textwrap
import time
from typing import IO, AnyStr, Final, Tuple
from collections.abc import Generator
from pathlib import Path
from typing import IO, TYPE_CHECKING, AnyStr, Final, Literal
from .util import CIProvider, detect_ci_provider
import humanize
FoldPattern = Tuple[str, str]
from cibuildwheel.ci import CIProvider, detect_ci_provider, filter_ansi_codes
if TYPE_CHECKING:
from cibuildwheel.options import Options
FoldPattern = tuple[str, str]
DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "")
FOLD_PATTERNS: Final[dict[str, FoldPattern]] = {
"azure": ("##[group]{name}", "##[endgroup]"),
@@ -23,28 +34,92 @@ PLATFORM_IDENTIFIER_DESCRIPTIONS: Final[dict[str, str]] = {
"manylinux_aarch64": "manylinux aarch64",
"manylinux_ppc64le": "manylinux ppc64le",
"manylinux_s390x": "manylinux s390x",
"manylinux_armv7l": "manylinux armv7l",
"manylinux_riscv64": "manylinux riscv64",
"musllinux_x86_64": "musllinux x86_64",
"musllinux_i686": "musllinux i686",
"musllinux_aarch64": "musllinux aarch64",
"musllinux_ppc64le": "musllinux ppc64le",
"musllinux_s390x": "manylinux s390x",
"musllinux_s390x": "musllinux s390x",
"musllinux_armv7l": "musllinux armv7l",
"musllinux_riscv64": "musllinux riscv64",
"win32": "Windows 32bit",
"win_amd64": "Windows 64bit",
"win_arm64": "Windows on ARM 64bit",
"macosx_x86_64": "macOS x86_64",
"macosx_universal2": "macOS Universal 2 - x86_64 and arm64",
"macosx_arm64": "macOS arm64 - Apple Silicon",
"pyodide_wasm32": "Pyodide",
"android_arm64_v8a": "Android arm64_v8a",
"android_x86_64": "Android x86_64",
"ios_arm64_iphoneos": "iOS Device (ARM64)",
"ios_arm64_iphonesimulator": "iOS Simulator (ARM64)",
"ios_x86_64_iphonesimulator": "iOS Simulator (x86_64)",
}
class Colors:
def __init__(self, *, enabled: bool) -> None:
self.red = "\033[31m" if enabled else ""
self.green = "\033[32m" if enabled else ""
self.yellow = "\033[33m" if enabled else ""
self.blue = "\033[34m" if enabled else ""
self.cyan = "\033[36m" if enabled else ""
self.bright_red = "\033[91m" if enabled else ""
self.bright_green = "\033[92m" if enabled else ""
self.white = "\033[37m\033[97m" if enabled else ""
self.gray = "\033[38;5;244m" if enabled else ""
self.bg_grey = "\033[48;5;235m" if enabled else ""
self.bold = "\033[1m" if enabled else ""
self.faint = "\033[2m" if enabled else ""
self.end = "\033[0m" if enabled else ""
class Symbols:
def __init__(self, *, unicode: bool) -> None:
self.done = "" if unicode else "done"
self.error = "" if unicode else "failed"
@dataclasses.dataclass(kw_only=True, frozen=True)
class BuildInfo:
identifier: str
filename: Path | None
duration: float
@functools.cached_property
def size(self) -> str | None:
if self.filename is None:
return None
return humanize.naturalsize(self.filename.stat().st_size)
@functools.cached_property
def sha256(self) -> str | None:
if self.filename is None:
return None
with self.filename.open("rb") as f:
digest = hashlib.file_digest(f, "sha256")
return digest.hexdigest()
def __str__(self) -> str:
duration = humanize.naturaldelta(self.duration)
if self.filename:
return f"{self.identifier}: {self.filename.name} {self.size} in {duration}, SHA256={self.sha256}"
return f"{self.identifier}: {duration} (test only)"
class Logger:
fold_mode: str
fold_mode: Literal["azure", "github", "travis", "disabled"]
colors_enabled: bool
unicode_enabled: bool
active_build_identifier: str | None = None
build_start_time: float | None = None
step_start_time: float | None = None
active_fold_group_name: str | None = None
summary: list[BuildInfo]
def __init__(self) -> None:
if sys.platform == "win32" and hasattr(sys.stdout, "reconfigure"):
@@ -56,25 +131,28 @@ class Logger:
ci_provider = detect_ci_provider()
if ci_provider == CIProvider.azure_pipelines:
self.fold_mode = "azure"
self.colors_enabled = True
match ci_provider:
case CIProvider.azure_pipelines:
self.fold_mode = "azure"
self.colors_enabled = True
elif ci_provider == CIProvider.github_actions:
self.fold_mode = "github"
self.colors_enabled = True
case CIProvider.github_actions:
self.fold_mode = "github"
self.colors_enabled = True
elif ci_provider == CIProvider.travis_ci:
self.fold_mode = "travis"
self.colors_enabled = True
case CIProvider.travis_ci:
self.fold_mode = "travis"
self.colors_enabled = True
elif ci_provider == CIProvider.appveyor:
self.fold_mode = "disabled"
self.colors_enabled = True
case CIProvider.appveyor:
self.fold_mode = "disabled"
self.colors_enabled = True
else:
self.fold_mode = "disabled"
self.colors_enabled = file_supports_color(sys.stdout)
case _:
self.fold_mode = "disabled"
self.colors_enabled = file_supports_color(sys.stdout)
self.summary = []
def build_start(self, identifier: str) -> None:
self.step_end()
@@ -88,7 +166,7 @@ class Logger:
self.build_start_time = time.time()
self.active_build_identifier = identifier
def build_end(self) -> None:
def build_end(self, filename: Path | None) -> None:
assert self.build_start_time is not None
assert self.active_build_identifier is not None
self.step_end()
@@ -96,11 +174,14 @@ class Logger:
c = self.colors
s = self.symbols
duration = time.time() - self.build_start_time
duration_str = humanize.naturaldelta(duration, minimum_unit="milliseconds")
print()
print(
f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s"
print(f"{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration_str}")
self.summary.append(
BuildInfo(identifier=self.active_build_identifier, filename=filename, duration=duration)
)
self.build_start_time = None
self.active_build_identifier = None
@@ -115,6 +196,7 @@ class Logger:
c = self.colors
s = self.symbols
duration = time.time() - self.step_start_time
if success:
print(f"{c.green}{s.done} {c.end}{duration:.2f}s".rjust(78))
else:
@@ -132,24 +214,48 @@ class Logger:
def notice(self, message: str) -> None:
if self.fold_mode == "github":
print(f"::notice::{message}\n", file=sys.stderr)
print(f"::notice::cibuildwheel: {message}\n", file=sys.stderr)
else:
c = self.colors
print(f"{c.bold}Note{c.end}: {message}\n", file=sys.stderr)
print(f"cibuildwheel: {c.bold}note{c.end}: {message}\n", file=sys.stderr)
def warning(self, message: str) -> None:
if self.fold_mode == "github":
print(f"::warning::{message}\n", file=sys.stderr)
print(f"::warning::cibuildwheel: {message}\n", file=sys.stderr)
else:
c = self.colors
print(f"{c.yellow}Warning{c.end}: {message}\n", file=sys.stderr)
print(f"cibuildwheel: {c.yellow}warning{c.end}: {message}\n", file=sys.stderr)
def error(self, error: BaseException | str) -> None:
if self.fold_mode == "github":
print(f"::error::{error}\n", file=sys.stderr)
print(f"::error::cibuildwheel: {error}\n", file=sys.stderr)
else:
c = self.colors
print(f"{c.bright_red}Error{c.end}: {error}\n", file=sys.stderr)
print(f"cibuildwheel: {c.bright_red}error{c.end}: {error}\n", file=sys.stderr)
@contextlib.contextmanager
def print_summary(self, *, options: "Options") -> Generator[None, None, None]:
start = time.time()
yield
duration = time.time() - start
if summary_path := os.environ.get("GITHUB_STEP_SUMMARY"):
github_summary = self._github_step_summary(duration=duration, options=options)
Path(summary_path).write_text(filter_ansi_codes(github_summary), encoding="utf-8")
n_wheels = len([info for info in self.summary if info.filename])
s = "s" if n_wheels > 1 else ""
duration_str = humanize.naturaldelta(duration)
print()
self._start_fold_group(f"{n_wheels} wheel{s} produced in {duration_str}")
for build_info in self.summary:
print(" ", build_info)
self._end_fold_group()
self.summary = []
@property
def step_active(self) -> bool:
return self.step_start_time is not None
def _start_fold_group(self, name: str) -> None:
self._end_fold_group()
@@ -186,6 +292,72 @@ class Logger:
# lowercase, shorten
return identifier.lower()[:20]
def _github_step_summary(self, duration: float, options: "Options") -> str:
"""
Returns the GitHub step summary, in markdown format.
"""
out = io.StringIO()
options_summary = options.summary(
identifiers=[bi.identifier for bi in self.summary], skip_unset=True
)
out.write(
textwrap.dedent("""\
### 🎡 cibuildwheel
<details>
<summary>
Build options
</summary>
```yaml
{options_summary}
```
</details>
""").format(options_summary=options_summary)
)
n_wheels = len([b for b in self.summary if b.filename])
wheel_rows = "\n".join(
"<tr>"
f"<td nowrap>{'<samp>' + b.filename.name + '</samp>' if b.filename else '*Test only*'}</td>"
f"<td nowrap>{b.size or 'N/A'}</td>"
f"<td nowrap><samp>{b.identifier}</samp></td>"
f"<td nowrap>{humanize.naturaldelta(b.duration)}</td>"
f"<td nowrap><samp>{b.sha256 or 'N/A'}</samp></td>"
"</tr>"
for b in self.summary
)
out.write(
textwrap.dedent("""\
<table>
<thead>
<tr>
<th align="left">Wheel</th>
<th align="left">Size</th>
<th align="left">Build identifier</th>
<th align="left">Time</th>
<th align="left">SHA256</th>
</tr>
</thead>
<tbody>
{wheel_rows}
</tbody>
</table>
<div align="right"><sup>{n} wheel{s} created in {duration_str}</sup></div>
""").format(
wheel_rows=wheel_rows,
n=n_wheels,
duration_str=humanize.naturaldelta(duration),
s="s" if n_wheels > 1 else "",
)
)
out.write("\n")
out.write("---")
out.write("\n")
return out.getvalue()
@property
def colors(self) -> Colors:
return Colors(enabled=self.colors_enabled)
@@ -201,17 +373,22 @@ def build_description_from_identifier(identifier: str) -> str:
build_description = ""
python_interpreter = python_identifier[0:2]
python_version = python_identifier[2:]
version_parts = python_identifier[2:].split("_")
python_version = version_parts[0]
if python_interpreter == "cp":
build_description += "CPython"
elif python_interpreter == "pp":
build_description += "PyPy"
elif python_interpreter == "gp":
build_description += "GraalPy"
else:
msg = f"unknown python {python_interpreter!r}"
raise Exception(msg)
build_description += f" {python_version[0]}.{python_version[1:]} "
if len(version_parts) > 1:
build_description += f"(ABI {version_parts[1]}) "
try:
build_description += PLATFORM_IDENTIFIER_DESCRIPTIONS[platform_identifier]
@@ -222,32 +399,6 @@ def build_description_from_identifier(identifier: str) -> str:
return build_description
class Colors:
def __init__(self, *, enabled: bool) -> None:
self.red = "\033[31m" if enabled else ""
self.green = "\033[32m" if enabled else ""
self.yellow = "\033[33m" if enabled else ""
self.blue = "\033[34m" if enabled else ""
self.cyan = "\033[36m" if enabled else ""
self.bright_red = "\033[91m" if enabled else ""
self.bright_green = "\033[92m" if enabled else ""
self.white = "\033[37m\033[97m" if enabled else ""
self.gray = "\033[38;5;244m" if enabled else ""
self.bg_grey = "\033[48;5;235m" if enabled else ""
self.bold = "\033[1m" if enabled else ""
self.faint = "\033[2m" if enabled else ""
self.end = "\033[0m" if enabled else ""
class Symbols:
def __init__(self, *, unicode: bool) -> None:
self.done = "" if unicode else "done"
self.error = "" if unicode else "failed"
def file_supports_color(file_obj: IO[AnyStr]) -> bool:
"""
Returns True if the running system's terminal supports color.
+184 -64
View File
@@ -1,5 +1,4 @@
from __future__ import annotations
import dataclasses
import io
import json
import os
@@ -8,35 +7,67 @@ import shlex
import shutil
import subprocess
import sys
import textwrap
import typing
import uuid
from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path, PurePath, PurePosixPath
from types import TracebackType
from typing import IO, Dict, Literal
from typing import IO, Literal, Self, assert_never
from ._compat.typing import Self
from .typing import PathOrStr, PopenBytes
from .util import (
CIProvider,
call,
detect_ci_provider,
parse_key_value_string,
strtobool,
)
from cibuildwheel.ci import CIProvider, detect_ci_provider
from cibuildwheel.errors import OCIEngineTooOldError
from cibuildwheel.logger import log
from cibuildwheel.typing import PathOrStr
from cibuildwheel.util.cmd import call
from cibuildwheel.util.helpers import FlexibleVersion, parse_key_value_string, strtobool
ContainerEngineName = Literal["docker", "podman"]
@dataclass(frozen=True)
# 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"
RISCV64 = "linux/riscv64"
S390X = "linux/s390x"
@classmethod
def native(cls) -> Self:
"""Return the current OCI platform, or raise ValueError if unknown."""
arch = platform.machine().lower()
mapping = {
"i386": cls.i386,
"i686": cls.i386,
"x86_64": cls.AMD64,
"amd64": cls.AMD64,
"armv7l": cls.ARMV7,
"aarch64": cls.ARM64,
"arm64": cls.ARM64,
"ppc64le": cls.PPC64LE,
"riscv64": cls.RISCV64,
"s390x": cls.S390X,
}
try:
return mapping[arch]
except KeyError as ex:
msg = f"Unsupported platform architecture: {arch}"
raise OSError(msg) from ex
@dataclasses.dataclass(frozen=True)
class OCIContainerEngineConfig:
name: ContainerEngineName
create_args: tuple[str, ...] = field(default_factory=tuple)
_: dataclasses.KW_ONLY
create_args: tuple[str, ...] = dataclasses.field(default_factory=tuple)
disable_host_mount: bool = False
@staticmethod
def from_config_string(config_string: str) -> OCIContainerEngineConfig:
@classmethod
def from_config_string(cls, config_string: str) -> Self:
config_dict = parse_key_value_string(
config_string,
["name"],
@@ -47,7 +78,7 @@ class OCIContainerEngineConfig:
msg = f"unknown container engine {name}"
raise ValueError(msg)
name = typing.cast(ContainerEngineName, name)
name = typing.cast("ContainerEngineName", name)
# some flexibility in the option names to cope with TOML conventions
create_args = config_dict.get("create_args") or config_dict.get("create-args") or []
disable_host_mount_options = (
@@ -56,10 +87,17 @@ class OCIContainerEngineConfig:
disable_host_mount = (
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(
name=name, create_args=tuple(create_args), disable_host_mount=disable_host_mount
)
return cls(name=name, create_args=tuple(create_args), disable_host_mount=disable_host_mount)
def options_summary(self) -> str | dict[str, str]:
if not self.create_args:
@@ -75,6 +113,58 @@ class OCIContainerEngineConfig:
DEFAULT_ENGINE = OCIContainerEngineConfig("docker")
def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
try:
version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True)
# We are using lowercase keys for all dicts so we are not affected by casing of keys
version_info = json.loads(
version_string.strip(), object_pairs_hook=lambda inp: {k.lower(): v for k, v in inp}
)
match engine.name:
case "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}.
"""
)
case "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}.
"""
)
case _:
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:
"""
An object that represents a running OCI (e.g. Docker) container.
@@ -87,20 +177,28 @@ class OCIContainer:
back to cibuildwheel.
Example:
>>> # xdoctest: +REQUIRES(LINUX)
>>> from cibuildwheel.oci_container import * # NOQA
>>> from cibuildwheel.options import _get_pinned_container_images
>>> import pytest
>>> try:
... oci_platform = OCIPlatform.native()
... except OSError as ex:
... pytest.skip(str(ex))
>>> if oci_platform != OCIPlatform.AMD64:
... pytest.skip('only runs on amd64')
>>> image = _get_pinned_container_images()['x86_64']['manylinux2014']
>>> # Test the default container
>>> with OCIContainer(image=image) as self:
>>> with OCIContainer(image=image, oci_platform=oci_platform) as self:
... self.call(["echo", "hello world"])
... self.call(["cat", "/proc/1/cgroup"])
... print(self.get_environment())
... print(self.debug_info())
"""
UTILITY_PYTHON = "/opt/python/cp38-cp38/bin/python"
UTILITY_PYTHON = "/opt/python/cp39-cp39/bin/python"
process: PopenBytes
process: subprocess.Popen[bytes]
bash_stdin: IO[bytes]
bash_stdout: IO[bytes]
@@ -108,7 +206,7 @@ class OCIContainer:
self,
*,
image: str,
enforce_32_bit: bool = False,
oci_platform: OCIPlatform,
cwd: PathOrStr | None = None,
engine: OCIContainerEngineConfig = DEFAULT_ENGINE,
):
@@ -117,14 +215,48 @@ class OCIContainer:
raise ValueError(msg)
self.image = image
self.enforce_32_bit = enforce_32_bit
self.oci_platform = oci_platform
self.cwd = cwd
self.name: str | None = None
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 reused (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:
self.name = f"cibuildwheel-{uuid.uuid4()}"
_check_engine_version(self.engine)
# work-around for Travis-CI PPC64le Docker runs since 2021:
# this avoids network splits
# https://github.com/pypa/cibuildwheel/issues/904
@@ -133,15 +265,30 @@ class OCIContainer:
if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le":
network_args = ["--network=host"]
platform_args = self._get_platform_args()
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
# or the image entrypoint takes care of enforcing this, then we don't need to
# simulate this
container_machine = call(
self.engine.name, "run", "--rm", self.image, "uname", "-m", capture_stdout=True
).strip()
simulate_32_bit = container_machine != "i686"
run_cmd = [self.engine.name, "run", "--rm"]
ctr_cmd = ["uname", "-m"]
try:
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"]
@@ -155,6 +302,7 @@ class OCIContainer:
"--interactive",
*(["--volume=/:/host"] if not self.engine.disable_host_mount else []),
*network_args,
*platform_args,
*self.engine.create_args,
self.image,
*shell_args,
@@ -221,21 +369,17 @@ class OCIContainer:
self.name = 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():
self.call(["mkdir", "-p", to_path])
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,
check=True,
cwd=from_path,
)
else:
exec_process: subprocess.Popen[bytes]
self.call(["mkdir", "-p", to_path.parent])
with subprocess.Popen(
[
self.engine.name,
@@ -250,8 +394,7 @@ class OCIContainer:
) as exec_process:
assert exec_process.stdin
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) # type: ignore[misc]
shutil.copyfileobj(from_file, exec_process.stdin)
exec_process.stdin.close()
exec_process.wait()
@@ -264,30 +407,7 @@ class OCIContainer:
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
# note: we assume from_path is a dir
to_path.mkdir(parents=True, exist_ok=True)
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)
call(self.engine.name, "cp", f"{self.name}:{from_path}/.", to_path)
def glob(self, path: PurePosixPath, pattern: str) -> list[PurePosixPath]:
glob_pattern = path.joinpath(pattern)
@@ -398,7 +518,7 @@ class OCIContainer:
capture_output=True,
)
)
return typing.cast(Dict[str, str], env)
return typing.cast("dict[str, str]", env)
def environment_executor(self, command: Sequence[str], environment: dict[str, str]) -> str:
# used as an EnvironmentExecutor to evaluate commands and capture output
+456 -243
View File
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
import sys
from collections.abc import Sequence
from pathlib import Path
from typing import Final, Protocol
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture
from cibuildwheel.options import Options
from cibuildwheel.platforms import android, ios, linux, macos, pyodide, windows
from cibuildwheel.selector import BuildSelector
from cibuildwheel.typing import GenericPythonConfiguration, PlatformName
class PlatformModule(Protocol):
# note that as per PEP544, the self argument is ignored when the protocol
# is applied to a module
def all_python_configurations(self) -> Sequence[GenericPythonConfiguration]: ...
def get_python_configurations(
self, build_selector: BuildSelector, architectures: set[Architecture]
) -> Sequence[GenericPythonConfiguration]: ...
def build(self, options: Options, tmp_path: Path) -> None: ...
ALL_PLATFORM_MODULES: Final[dict[PlatformName, PlatformModule]] = {
"linux": linux,
"windows": windows,
"macos": macos,
"pyodide": pyodide,
"android": android,
"ios": ios,
}
def native_platform() -> PlatformName:
if sys.platform.startswith("linux"):
return "linux"
elif sys.platform == "darwin":
return "macos"
elif sys.platform == "win32":
return "windows"
else:
msg = (
'Unable to detect platform from "sys.platform". cibuildwheel doesn\'t '
"support building wheels for this platform. You might be able to build for a different "
"platform using the --platform argument. Check --help output for more information."
)
raise errors.ConfigurationError(msg)
def get_build_identifiers(
platform_module: PlatformModule,
build_selector: BuildSelector,
architectures: set[Architecture],
) -> list[str]:
python_configurations = platform_module.get_python_configurations(build_selector, architectures)
return [config.identifier for config in python_configurations]
+757
View File
@@ -0,0 +1,757 @@
import csv
import hashlib
import os
import platform
import re
import shlex
import shutil
import subprocess
import sysconfig
from collections.abc import Iterable, Iterator, MutableMapping
from dataclasses import dataclass
from os.path import relpath
from pathlib import Path
from pprint import pprint
from runpy import run_path
from textwrap import dedent
from typing import Any
from build import ProjectBuilder
from build.env import IsolatedEnv
from elftools.common.exceptions import ELFError
from elftools.elf.elffile import ELFFile
from filelock import FileLock
from cibuildwheel import errors, platforms # pylint: disable=cyclic-import
from cibuildwheel.architecture import Architecture, arch_synonym
from cibuildwheel.frontend import get_build_frontend_extra_flags, parse_config_settings
from cibuildwheel.logger import log
from cibuildwheel.options import BuildOptions, Options
from cibuildwheel.selector import BuildSelector
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call, shell
from cibuildwheel.util.file import CIBW_CACHE_PATH, copy_test_sources, download, move_file
from cibuildwheel.util.helpers import prepare_command
from cibuildwheel.util.packaging import find_compatible_wheel
from cibuildwheel.util.python_build_standalone import create_python_build_standalone_environment
from cibuildwheel.venv import constraint_flags, find_uv, virtualenv
ANDROID_TRIPLET = {
"arm64_v8a": "aarch64-linux-android",
"x86_64": "x86_64-linux-android",
}
def parse_identifier(identifier: str) -> tuple[str, str]:
match = re.fullmatch(r"cp(\d)(\d+)-android_(.+)", identifier)
if not match:
msg = f"invalid Android identifier: '{identifier}'"
raise ValueError(msg)
major, minor, arch = match.groups()
return (f"{major}.{minor}", arch)
def android_triplet(identifier: str) -> str:
return ANDROID_TRIPLET[parse_identifier(identifier)[1]]
@dataclass(frozen=True)
class PythonConfiguration:
version: str
identifier: str
url: str
@property
def arch(self) -> str:
return parse_identifier(self.identifier)[1]
def all_python_configurations() -> list[PythonConfiguration]:
return [PythonConfiguration(**item) for item in resources.read_python_configs("android")]
def get_python_configurations(
build_selector: BuildSelector, architectures: set[Architecture]
) -> list[PythonConfiguration]:
return [
c
for c in all_python_configurations()
if c.arch in architectures and build_selector(c.identifier)
]
def shell_prepared(command: str, *, build_options: BuildOptions, env: dict[str, str]) -> None:
shell(
prepare_command(command, project=".", package=build_options.package_dir),
env=env,
)
def before_all(options: Options, python_configurations: list[PythonConfiguration]) -> None:
before_all_options = options.build_options(python_configurations[0].identifier)
if before_all_options.before_all:
log.step("Running before_all...")
shell_prepared(
before_all_options.before_all,
build_options=before_all_options,
env=before_all_options.environment.as_dictionary(os.environ),
)
@dataclass(frozen=True)
class BuildState:
config: PythonConfiguration
options: BuildOptions
build_path: Path
python_dir: Path
build_env: dict[str, str]
android_env: dict[str, str]
def build(options: Options, tmp_path: Path) -> None:
if "ANDROID_HOME" not in os.environ:
msg = (
"ANDROID_HOME environment variable is not set. For instructions, see "
"https://cibuildwheel.pypa.io/en/stable/platforms/#android"
)
raise errors.FatalError(msg)
configs = get_python_configurations(
options.globals.build_selector, options.globals.architectures
)
if not configs:
return
try:
before_all(options, configs)
built_wheels: list[Path] = []
for config in configs:
log.build_start(config.identifier)
build_options = options.build_options(config.identifier)
build_path = tmp_path / config.identifier
build_path.mkdir()
python_dir = setup_target_python(config, build_path)
build_env, android_env = setup_env(config, build_options, build_path, python_dir)
state = BuildState(
config, build_options, build_path, python_dir, build_env, android_env
)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
print(
f"\nFound previously built wheel {compatible_wheel.name} that is "
f"compatible with {config.identifier}. Skipping build step..."
)
repaired_wheel = compatible_wheel
else:
before_build(state)
built_wheel = build_wheel(state)
repaired_wheel = repair_wheel(state, built_wheel)
test_wheel(state, repaired_wheel, build_frontend=build_options.build_frontend.name)
output_wheel: Path | None = None
if compatible_wheel is None:
output_wheel = move_file(
repaired_wheel, build_options.output_dir / repaired_wheel.name
)
built_wheels.append(output_wheel)
shutil.rmtree(build_path)
log.build_end(output_wheel)
except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
def setup_target_python(config: PythonConfiguration, build_path: Path) -> Path:
log.step("Installing target Python...")
python_tgz = CIBW_CACHE_PATH / config.url.rpartition("/")[-1]
with FileLock(f"{python_tgz}.lock"):
if not python_tgz.exists():
download(config.url, python_tgz)
python_dir = build_path / "python"
python_dir.mkdir()
shutil.unpack_archive(python_tgz, python_dir)
return python_dir
def setup_env(
config: PythonConfiguration, build_options: BuildOptions, build_path: Path, python_dir: Path
) -> tuple[dict[str, str], dict[str, str]]:
"""
Returns two environment dicts, both pointing at the same virtual environment:
* build_env, which uses the environment normally.
* android_env, which uses the environment while simulating running on Android.
"""
log.step("Setting up build environment...")
build_frontend = build_options.build_frontend.name
use_uv = build_frontend in {"build[uv]", "uv"}
uv_path = find_uv()
if use_uv and uv_path is None:
msg = "uv not found"
raise AssertionError(msg)
pip = ["pip"] if not use_uv else [str(uv_path), "pip"]
# Create virtual environment
python_exe = create_python_build_standalone_environment(
config.version, build_path, CIBW_CACHE_PATH
)
venv_dir = build_path / "venv"
dependency_constraint = build_options.dependency_constraints.get_for_python_version(
version=config.version, tmp_dir=build_path
)
build_env = virtualenv(
config.version, python_exe, venv_dir, dependency_constraint, use_uv=use_uv
)
create_cmake_toolchain(config, build_path, python_dir, build_env)
# Apply custom environment variables, and check environment is still valid
build_env = build_options.environment.as_dictionary(build_env)
build_env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
for command in ["python"] if use_uv else ["python", "pip"]:
command_path = call("which", command, env=build_env, capture_stdout=True).strip()
if command_path != f"{venv_dir}/bin/{command}":
msg = (
f"{command} available on PATH doesn't match our installed instance. If you "
f"have modified PATH, ensure that you don't overwrite cibuildwheel's entry "
f"or insert {command} above it."
)
raise errors.FatalError(msg)
call(command, "--version", env=build_env)
# Construct an altered environment which simulates running on Android.
android_env = setup_android_env(config, python_dir, venv_dir, build_env)
# Install build tools
if build_frontend not in {"build", "build[uv]", "uv"}:
msg = "Android requires the build frontend to be 'build' or 'uv'"
raise errors.FatalError(msg)
call(*pip, "install", "build", *constraint_flags(dependency_constraint), env=build_env)
# Build-time requirements must be queried within android_env, because
# `get_requires_for_build` can run arbitrary code in setup.py scripts, which may be
# affected by the target platform. However, the requirements must be installed
# within build_env, because they're going to run on the build machine.
#
# The `build` CLI doesn't support this combination, so we use its API to query the
# requirements, and then install them ourselves with pip. We'll later run `build` in
# the same environment, passing the `--no-isolation` option.
class AndroidEnv(IsolatedEnv):
@property
def python_executable(self) -> str:
return f"{venv_dir}/bin/python"
def make_extra_environ(self) -> dict[str, str]:
return android_env
pb = ProjectBuilder.from_isolated_env(AndroidEnv(), build_options.package_dir)
if pb.build_system_requires:
call(*pip, "install", *pb.build_system_requires, env=build_env)
requires_for_build = pb.get_requires_for_build(
"wheel", parse_config_settings(build_options.config_settings)
)
if requires_for_build:
call(*pip, "install", *requires_for_build, env=build_env)
return build_env, android_env
def create_cmake_toolchain(
config: PythonConfiguration, build_path: Path, python_dir: Path, build_env: dict[str, str]
) -> None:
toolchain_path = build_path / "toolchain.cmake"
build_env["CMAKE_TOOLCHAIN_FILE"] = str(toolchain_path)
with open(toolchain_path, "w", encoding="UTF-8") as toolchain_file:
print(
dedent(
f"""\
# To support as many build systems as possible, we use environment
# variables as the single source of truth for compiler flags and paths,
# so they don't need to be specified here.
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_PROCESSOR {android_triplet(config.identifier).split("-")[0]})
# Inhibit all of CMake's own NDK handling code.
set(CMAKE_SYSTEM_VERSION 1)
# Tell CMake where to look for headers and libraries.
set(CMAKE_FIND_ROOT_PATH "{python_dir}/prefix")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
# Allow CMake to run Python in the simulated Android environment when
# policy CMP0190 is active.
set(CMAKE_CROSSCOMPILING_EMULATOR /bin/sh -c [["$0" "$@"]])
"""
),
file=toolchain_file,
)
def localize_sysconfigdata(
python_dir: Path, build_env: dict[str, str], sysconfigdata_path: Path
) -> dict[str, Any]:
sysconfigdata: dict[str, Any] = run_path(str(sysconfigdata_path))["build_time_vars"]
with sysconfigdata_path.open("w", encoding="UTF-8") as f:
f.write("# Generated by cibuildwheel\n")
f.write("build_time_vars = ")
sysconfigdata = localized_vars(build_env, sysconfigdata, python_dir / "prefix")
pprint(sysconfigdata, stream=f, compact=True)
return sysconfigdata
def localized_vars(
build_env: dict[str, str], orig_vars: dict[str, Any], prefix: Path
) -> dict[str, Any]:
orig_prefix = orig_vars["prefix"]
localized_vars_ = {}
for key, value in orig_vars.items():
# The host's sysconfigdata will include references to build-time paths.
# Update these to refer to the current prefix.
final = value
if isinstance(final, str):
final = final.replace(orig_prefix, str(prefix))
if key == "ANDROID_API_LEVEL":
if api_level := build_env.get(key):
final = int(api_level)
# Build systems vary in whether FLAGS variables are read from sysconfig, and if so,
# whether they're replaced by environment variables or combined with them. Even
# setuptools has changed its behavior here
# (https://github.com/pypa/setuptools/issues/4836).
#
# Ensure consistency by clearing the sysconfig variables and letting the environment
# variables take effect alone. This will also work for any non-Python build systems
# which the build script may call.
elif key in ["CFLAGS", "CXXFLAGS", "LDFLAGS"]:
final = ""
# These variables contain an embedded copy of LDFLAGS.
elif key in ["LDSHARED", "LDCXXSHARED"]:
final = final.removesuffix(" " + orig_vars["LDFLAGS"])
localized_vars_[key] = final
return localized_vars_
def setup_android_env(
config: PythonConfiguration, python_dir: Path, venv_dir: Path, build_env: dict[str, str]
) -> dict[str, str]:
site_packages = next(venv_dir.glob("lib/python*/site-packages"))
for suffix in ["pth", "py"]:
shutil.copy(resources.PATH / f"_cross_venv.{suffix}", site_packages)
sysconfigdata_path = Path(
shutil.copy(
next(python_dir.glob("prefix/lib/python*/_sysconfigdata_*.py")),
site_packages,
)
)
sysconfigdata = localize_sysconfigdata(python_dir, build_env, sysconfigdata_path)
# Activate the code in _cross_venv.py.
android_env = build_env.copy()
android_env["CIBW_HOST_TRIPLET"] = android_triplet(config.identifier)
# Get the environment variables needed to build for Android (CC, CFLAGS, etc). These are
# generated by https://github.com/python/cpython/blob/main/Android/android-env.sh.
env_output = call(python_dir / "android.py", "env", env=build_env, capture_stdout=True)
# shlex.split should produce a sequence alternating between:
# * the word "export"
# * a key=value string, without quotes
for i, token in enumerate(shlex.split(env_output)):
if i % 2 == 0:
assert token == "export", token
else:
key, sep, value = token.partition("=")
assert sep == "=", token
android_env[key] = value
# localized_vars cleared the CFLAGS and CXXFLAGS in the sysconfigdata, but most
# packages take their optimization flags from these variables. Pass these flags via
# environment variables instead.
#
# We don't enable debug information, because it significantly increases binary size,
# and most Android app developers don't have the NDK installed, so they would have no
# way to strip it.
opt = " ".join(word for word in sysconfigdata["OPT"].split() if not word.startswith("-g"))
for key in ["CFLAGS", "CXXFLAGS"]:
android_env[key] += " " + opt
# Cargo target linker needs to be specified after CC is set
setup_rust(config, python_dir, android_env)
# Format the environment so it can be pasted into a shell when debugging.
for key, value in sorted(android_env.items()):
if os.environ.get(key) != value:
print(f"export {key}={shlex.quote(value)}")
return android_env
def setup_rust(
config: PythonConfiguration,
python_dir: Path,
env: MutableMapping[str, str],
) -> None:
cargo_target = android_triplet(config.identifier)
# CARGO_BUILD_TARGET is the variable used by Cargo and setuptools_rust
env["CARGO_BUILD_TARGET"] = cargo_target
# The linker needs to be specified after CC is set by android-env.sh
cargo_target_linker_env_name = f"CARGO_TARGET_{cargo_target.upper().replace('-', '_')}_LINKER"
# CC has already been set by calling android.py (it calls android-env.sh)
env[cargo_target_linker_env_name] = env["CC"]
# All Python extension modules must be explicitly linked against libpython3.x.so when building for Android.
# See: https://peps.python.org/pep-0738/#linkage
# For projects using PyO3, this requires setting PYO3_CROSS_LIB_DIR to the directory containing libpython3.x.so.
# See: https://pyo3.rs/v0.27.1/building-and-distribution.html#cross-compiling
env["PYO3_CROSS_LIB_DIR"] = str(python_dir / "prefix" / "lib")
venv_bin = Path(env["VIRTUAL_ENV"]) / "bin"
for tool in ["cargo", "rustup"]:
shim_path = venv_bin / tool
shutil.copy(resources.PATH / "_rust_shim.py", shim_path)
shim_path.chmod(0o755)
def before_build(state: BuildState) -> None:
if state.options.before_build:
log.step("Running before_build...")
shell_prepared(
state.options.before_build,
build_options=state.options,
env=state.build_env,
)
def build_wheel(state: BuildState) -> Path:
log.step("Building wheel...")
built_wheel_dir = state.build_path / "built_wheel"
match state.options.build_frontend.name:
case "build" | "build[uv]":
call(
"python",
"-m",
"build",
state.options.package_dir,
"--wheel",
"--no-isolation",
"--skip-dependency-check",
f"--outdir={built_wheel_dir}",
*get_build_frontend_extra_flags(
state.options.build_frontend,
state.options.build_verbosity,
state.options.config_settings,
py38=False,
),
env=state.android_env,
)
case "uv":
uv_path = find_uv()
assert uv_path is not None
call(
uv_path,
"build",
state.options.package_dir,
"--wheel",
"--no-build-isolation",
f"--out-dir={built_wheel_dir}",
*get_build_frontend_extra_flags(
state.options.build_frontend,
state.options.build_verbosity,
state.options.config_settings,
py38=False,
),
env=state.android_env,
)
case x:
msg = f"Invalid build backend {x!r}"
raise AssertionError(msg)
built_wheels = list(built_wheel_dir.glob("*.whl"))
if len(built_wheels) != 1:
msg = f"{built_wheel_dir} contains {len(built_wheels)} wheels; expected 1"
raise errors.FatalError(msg)
built_wheel = built_wheels[0]
if built_wheel.name.endswith("none-any.whl"):
raise errors.NonPlatformWheelError()
return built_wheel
def repair_wheel(state: BuildState, built_wheel: Path) -> Path:
log.step("Repairing wheel...")
repaired_wheel_dir = state.build_path / "repaired_wheel"
repaired_wheel_dir.mkdir()
if state.options.repair_command:
shell(
prepare_command(
state.options.repair_command,
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
package=state.options.package_dir,
project=".",
),
env=state.build_env,
)
else:
repair_default(state.android_env, built_wheel, repaired_wheel_dir)
repaired_wheels = list(repaired_wheel_dir.glob("*.whl"))
if len(repaired_wheels) == 0:
raise errors.RepairStepProducedNoWheelError()
if len(repaired_wheels) != 1:
msg = f"{repaired_wheel_dir} contains {len(repaired_wheels)} wheels; expected 1"
raise errors.FatalError(msg)
repaired_wheel = repaired_wheels[0]
if repaired_wheel.name.endswith("none-any.whl"):
raise errors.NonPlatformWheelError()
return repaired_wheel
def repair_default(
android_env: dict[str, str], built_wheel: Path, repaired_wheel_dir: Path
) -> None:
"""
Adds libc++ to the wheel if anything links against it. In the future this should be
moved to auditwheel and generalized to support more libraries.
"""
if (match := re.search(r"^(.+?)-", built_wheel.name)) is None:
msg = f"Failed to parse wheel filename: {built_wheel.name}"
raise errors.FatalError(msg)
wheel_name = match[1]
unpacked_dir = repaired_wheel_dir / "unpacked"
unpacked_dir.mkdir()
shutil.unpack_archive(built_wheel, unpacked_dir, format="zip")
# Some build systems are inconsistent about name normalization, so don't assume the
# dist-info name is identical to the wheel name.
record_paths = list(unpacked_dir.glob("*.dist-info/RECORD"))
if len(record_paths) != 1:
msg = f"{built_wheel.name} contains {len(record_paths)} dist-info/RECORD files; expected 1"
raise errors.FatalError(msg)
old_soname = "libc++_shared.so"
paths_to_patch = []
for path, elffile in elf_file_filter(
unpacked_dir / filename
for filename, *_ in csv.reader(record_paths[0].read_text().splitlines())
):
if (dynamic := elffile.get_section_by_name(".dynamic")) and any( # type: ignore[no-untyped-call]
tag.entry.d_tag == "DT_NEEDED" and tag.needed == old_soname
for tag in dynamic.iter_tags()
):
paths_to_patch.append(path)
if not paths_to_patch:
shutil.copyfile(built_wheel, repaired_wheel_dir / built_wheel.name)
else:
# Android doesn't support DT_RPATH, but supports DT_RUNPATH since API level 24
# (https://github.com/aosp-mirror/platform_bionic/blob/master/android-changes-for-ndk-developers.md).
if int(sysconfig_print('get_config_vars()["ANDROID_API_LEVEL"]', android_env)) < 24:
msg = f"Adding {old_soname} requires ANDROID_API_LEVEL to be at least 24"
raise errors.FatalError(msg)
toolchain = Path(android_env["CC"]).parent.parent
src_path = toolchain / f"sysroot/usr/lib/{android_env['CIBW_HOST_TRIPLET']}/{old_soname}"
# Use the same library location as auditwheel would.
libs_dir = unpacked_dir / (wheel_name + ".libs")
libs_dir.mkdir()
new_soname = soname_with_hash(src_path)
dst_path = libs_dir / new_soname
shutil.copyfile(src_path, dst_path)
call(which("patchelf"), "--set-soname", new_soname, dst_path)
for path in paths_to_patch:
call(which("patchelf"), "--replace-needed", old_soname, new_soname, path)
call(
which("patchelf"),
"--set-rpath",
f"${{ORIGIN}}/{relpath(libs_dir, path.parent)}",
path,
)
call(which("wheel"), "pack", unpacked_dir, "-d", repaired_wheel_dir)
# If cibuildwheel was called without activating its environment, its scripts directory
# will not be on the PATH.
def which(cmd: str) -> str:
scripts_dir = sysconfig.get_path("scripts")
result = shutil.which(cmd, path=scripts_dir + os.pathsep + os.environ["PATH"])
if result is None:
msg = f"Couldn't find {cmd!r} in {scripts_dir} or on the PATH"
raise errors.FatalError(msg)
return result
def elf_file_filter(paths: Iterable[Path]) -> Iterator[tuple[Path, ELFFile]]:
"""Filter through an iterator of filenames and load up only ELF files"""
for path in paths:
if not path.name.endswith(".py"):
try:
with open(path, "rb") as f:
candidate = ELFFile(f) # type: ignore[no-untyped-call]
yield path, candidate
except ELFError:
pass # Not an ELF file
def soname_with_hash(src_path: Path) -> str:
"""Return the same library filename as auditwheel would"""
shorthash = hashlib.sha256(src_path.read_bytes()).hexdigest()[:8]
src_name = src_path.name
base, ext = src_name.split(".", 1)
if not base.endswith(f"-{shorthash}"):
return f"{base}-{shorthash}.{ext}"
else:
return src_name
def test_wheel(state: BuildState, wheel: Path, *, build_frontend: str) -> None:
test_command = state.options.test_command
if not (test_command and state.options.test_selector(state.config.identifier)):
return
log.step("Testing wheel...")
use_uv = build_frontend == "build[uv]"
uv_path = find_uv()
if use_uv and uv_path is None:
msg = "uv not found"
raise AssertionError(msg)
pip = ["pip"] if not use_uv else [str(uv_path), "pip"]
native_arch = arch_synonym(platform.machine(), platforms.native_platform(), "android")
if state.config.arch != native_arch:
log.warning(
f"Skipping tests for {state.config.arch}, as the build machine only "
f"supports {native_arch}"
)
return
if state.options.before_test:
shell_prepared(
state.options.before_test,
build_options=state.options,
env=state.build_env,
)
platform_args = (
["--python-platform", android_triplet(state.config.identifier)]
if use_uv
else [
"--platform",
sysconfig_print("get_platform()", state.android_env).replace("-", "_"),
]
)
# Install the wheel and test-requires.
site_packages_dir = state.build_path / "site-packages"
site_packages_dir.mkdir()
call(
*pip,
"install",
"--only-binary=:all:",
*platform_args,
"--target",
site_packages_dir,
f"{wheel}{state.options.test_extras}",
*state.options.test_requires,
env=state.android_env,
)
# Copy test-sources.
cwd_dir = state.build_path / "cwd"
cwd_dir.mkdir()
if state.options.test_sources:
copy_test_sources(state.options.test_sources, Path.cwd(), cwd_dir)
else:
(cwd_dir / "test_fail.py").write_text(
resources.TEST_FAIL_CWD_FILE.read_text(),
)
# Android doesn't support placeholders in the test command.
if any(("{" + placeholder + "}") in test_command for placeholder in ("project", "package")):
msg = (
f"Test command {test_command!r} with a "
"'{project}' or '{package}' placeholder is not supported on Android, "
"because the source directory is not visible on the emulator."
)
raise errors.FatalError(msg)
# Parse test-command.
test_args = shlex.split(test_command)
if test_args[0] in {"python", "python3"} and any(arg in test_args for arg in ("-c", "-m")):
# Forward the args to the CPython testbed script. We require '-c' or '-m'
# to be in the command, because without those flags, the testbed script
# will prepend '-m test', which will run Python's own test suite.
del test_args[0]
elif test_args[0] == "pytest":
# We transform some commands into the `python -m` form, but this is deprecated.
msg = (
f"Test command {test_command!r} is not supported on Android. "
"cibuildwheel will try to execute it as if it started with 'python -m'. "
"If this works, all you need to do is add that to your test command."
)
log.warning(msg)
test_args.insert(0, "-m")
else:
msg = (
f"Test command {test_command!r} is not supported on Android. "
f"Command must begin with 'python' or 'python3', and contain '-m' or '-c'."
)
raise errors.FatalError(msg)
# By default, run on a testbed managed emulator running the newest supported
# Android version. However, if the user specifies a --managed or --connected
# test execution argument, that argument takes precedence.
test_runtime_args = state.options.test_runtime.args
if any(arg.startswith(("--managed", "--connected")) for arg in test_runtime_args):
emulator_args = []
else:
emulator_args = ["--managed", "maxVersion"]
# Run the test app.
call(
state.python_dir / "android.py",
"test",
"--site-packages",
site_packages_dir,
"--cwd",
cwd_dir,
*emulator_args,
*(["-v"] if state.options.build_verbosity > 0 else []),
*test_runtime_args,
"--",
*test_args,
env=state.build_env,
)
def sysconfig_print(method_call: str, env: dict[str, str]) -> str:
return call(
"python",
"-c",
f'import sysconfig; print(sysconfig.{method_call}, end="")',
env=env,
capture_stdout=True,
)
+725
View File
@@ -0,0 +1,725 @@
import dataclasses
import os
import platform
import shlex
import shutil
import subprocess
import sys
import textwrap
from collections.abc import Sequence, Set
from pathlib import Path
from typing import assert_never
from filelock import FileLock
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture
from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.frontend import BuildFrontendName, get_build_frontend_extra_flags
from cibuildwheel.logger import log
from cibuildwheel.options import Options
from cibuildwheel.platforms.macos import install_cpython as install_build_cpython
from cibuildwheel.selector import BuildSelector
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call, shell, split_command
from cibuildwheel.util.file import CIBW_CACHE_PATH, copy_test_sources, download, move_file
from cibuildwheel.util.helpers import prepare_command, unwrap_preserving_paragraphs
from cibuildwheel.util.packaging import find_compatible_wheel
from cibuildwheel.venv import constraint_flags, virtualenv
@dataclasses.dataclass(frozen=True, kw_only=True)
class PythonConfiguration:
version: str
identifier: str
url: str
build_url: str
@property
def sdk(self) -> str:
return self.multiarch.rsplit("-", 1)[1]
@property
def arch(self) -> str:
return self.multiarch.rsplit("-", 1)[0]
@property
def multiarch(self) -> str:
# The multiarch identifier, as reported by `sys.implementation._multiarch`
return "-".join(self.identifier.split("-ios_")[1].rsplit("_", 1))
@property
def is_simulator(self) -> bool:
return self.identifier.endswith("_iphonesimulator")
@property
def xcframework_slice(self) -> str:
"XCframeworks include binaries for multiple ABIs; which ABI section should be used?"
return "ios-arm64_x86_64-simulator" if self.is_simulator else "ios-arm64"
def all_python_configurations() -> list[PythonConfiguration]:
# iOS builds are always cross builds; we need to install a macOS Python as
# well. Rather than duplicate the location of the URL of macOS installers,
# load the macos configurations, determine the macOS configuration that
# matches the platform we're building, and embed that URL in the parsed iOS
# configuration.
macos_python_configs = resources.read_python_configs("macos")
def build_url(config_dict: dict[str, str]) -> str:
# The iOS identifier will be something like cp313-ios_arm64_iphoneos.
# Drop the iphoneos suffix, then replace ios with macosx to yield
# cp313-macosx_arm64, which will be a macOS build identifier.
modified_ios_identifier = config_dict["identifier"].rsplit("_", 1)[0]
macos_identifier = modified_ios_identifier.replace("ios", "macosx")
matching = [
config for config in macos_python_configs if config["identifier"] == macos_identifier
]
return matching[0]["url"]
# Load the platform configuration
full_python_configs = resources.read_python_configs("ios")
# Build the configurations, annotating with macOS URL details.
return [
PythonConfiguration(
**item,
build_url=build_url(item),
)
for item in full_python_configs
]
def get_python_configurations(
build_selector: BuildSelector,
architectures: Set[Architecture],
) -> list[PythonConfiguration]:
python_configurations = all_python_configurations()
# Filter out configs that don't match any of the selected architectures
python_configurations = [
c
for c in python_configurations
if any(c.identifier.endswith(f"-ios_{a.value}") for a in architectures)
]
# Skip builds as required by BUILD/SKIP
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
return python_configurations
def install_target_cpython(tmp: Path, config: PythonConfiguration, free_threading: bool) -> Path:
if free_threading:
msg = "Free threading builds aren't available for iOS (yet)"
raise errors.FatalError(msg)
# Install an iOS build of CPython
ios_python_tar_gz = config.url.rsplit("/", 1)[-1]
extension = ".tar.gz"
assert ios_python_tar_gz.endswith(extension)
installation_path = CIBW_CACHE_PATH / ios_python_tar_gz[: -len(extension)]
with FileLock(str(installation_path) + ".lock"):
if not installation_path.exists():
downloaded_tar_gz = tmp / ios_python_tar_gz
download(config.url, downloaded_tar_gz)
installation_path.mkdir(parents=True, exist_ok=True)
call("tar", "-C", installation_path, "-xf", downloaded_tar_gz)
downloaded_tar_gz.unlink()
return installation_path
def cross_virtualenv(
*,
py_version: str,
target_python: Path,
multiarch: str,
build_python: Path,
venv_path: Path,
dependency_constraint: Path | None,
xbuild_tools: Sequence[str] | None,
) -> dict[str, str]:
"""Create a cross-compilation virtual environment.
In a cross-compilation environment, the *target* is the platform where the
code will ultimately run, and the *build* is the platform where you're
running the compilation. When building iOS wheels, iOS is the target machine
and macOS is the build machine. The terminology around these machines varies
between build tools (configure uses "host" and "build"; cmake uses "target" and
"build host").
A cross-compilation virtualenv is an environment that is based on the
*build* python (so that binaries can execute); but it modifies the
environment at startup so that any request for platform details (such as
`sys.platform` or `sysconfig.get_platform()`) return details of the target
platform. It also applies a loader patch so that any virtualenv created by
the cross-compilation environment will also be a cross-compilation
environment.
:param py_version: The Python version (major.minor) in use
:param target_python: The path to the python binary for the target platform
:param multiarch: The multiarch tag for the target platform (i.e., the value
of `sys.implementation._multiarch`)
:param build_python: The path to the python binary for the build platform
:param venv_path: The path where the cross virtual environment should be
created.
:param dependency_constraint: A path to a constraint file that should be
used when constraining dependencies in the environment.
:param xbuild_tools: A list of executable names (without paths) that are
on the path, but must be preserved in the cross environment.
"""
# Create an initial macOS virtual environment
env = virtualenv(
py_version,
build_python,
venv_path,
dependency_constraint,
use_uv=False,
)
# Convert the macOS virtual environment into an iOS virtual environment
# using the cross-platform conversion script in the iOS distribution.
# target_python is the path to the Python binary;
# determine the root of the XCframework slice that is being used.
slice_path = target_python.parent.parent
call(
"python",
str(slice_path / f"platform-config/{multiarch}/make_cross_venv.py"),
str(venv_path),
env=env,
cwd=venv_path,
)
# When running on macOS, it's easy for the build environment to leak into
# the target environment, especially when building for ARM64 (because the
# build architecture is the same as the target architecture). The primary
# culprit for this is Homebrew libraries leaking in as dependencies for iOS
# libraries.
#
# To prevent problems, set the PATH to isolate the build environment from
# sources that could introduce incompatible binaries.
#
# However, there may be some tools on the path that are needed for the
# build. Find their location on the path, and link the underlying binaries
# (fully resolving symlinks) to a "safe" location that will *only* contain
# those tools. This avoids needing to add *all* of Homebrew to the path just
# to get access to (for example) cmake for build purposes. A value of None
# means the user hasn't provided a list of xbuild tools.
xbuild_tools_path = venv_path / "cibw_xbuild_tools"
xbuild_tools_path.mkdir()
if xbuild_tools is None:
log.warning(
textwrap.dedent(
"""
Your project configuration does not define any cross-build tools.
iOS builds use an isolated build environment; if your build process requires any
third-party tools (such as cmake, ninja, or rustc), you must explicitly declare
that those tools are required using xbuild-tools/CIBW_XBUILD_TOOLS. This will
likely manifest as a "somebuildtool: command not found" error.
If the build succeeds, you can silence this warning by setting adding
`xbuild-tools = []` to your pyproject.toml configuration, or exporting
CIBW_XBUILD_TOOLS as an empty string into your environment.
"""
)
)
else:
for tool in xbuild_tools:
tool_path = shutil.which(tool)
if tool_path is None:
msg = f"Could not find a {tool!r} executable on the path."
raise errors.FatalError(msg)
# Link the binary into the safe tools directory
original = Path(tool_path).resolve()
print(f"{tool!r} will be included in the cross-build environment (using {original})")
(xbuild_tools_path / tool).symlink_to(original)
env["PATH"] = os.pathsep.join(
[
# The target python's binary directory
str(target_python.parent),
# The cross-platform environment's binary directory
str(venv_path / "bin"),
# The directory of cross-build tools
str(xbuild_tools_path),
# The bare minimum Apple system paths.
"/usr/bin",
"/bin",
"/usr/sbin",
"/sbin",
"/Library/Apple/usr/bin",
]
)
# Also unset DYLD_LIBRARY_PATH to ensure that no macOS libraries will be
# found and linked.
env.pop("DYLD_LIBRARY_PATH", None)
return env
def setup_python(
tmp: Path,
*,
python_configuration: PythonConfiguration,
dependency_constraint: Path | None,
environment: ParsedEnvironment,
build_frontend: BuildFrontendName,
xbuild_tools: Sequence[str] | None,
) -> tuple[Path, dict[str, str]]:
# Not using set because mypy can't narrow it
if build_frontend == "build[uv]" or build_frontend == "uv": # noqa: PLR1714
msg = "uv doesn't support iOS"
raise errors.FatalError(msg)
# An iOS environment requires 2 python installs - one for the build machine
# (macOS), and one for the target (iOS). We'll only ever interact with the
# *target* python, but the build Python needs to exist to act as the base
# for a cross venv.
tmp.mkdir()
implementation_id = python_configuration.identifier.split("-")[0]
log.step(f"Installing Build Python {implementation_id}...")
if implementation_id.startswith("cp"):
free_threading = "t-ios" in python_configuration.identifier
build_python = install_build_cpython(
tmp,
python_configuration.version,
python_configuration.build_url,
free_threading,
)
else:
msg = f"Unknown Python implementation: {implementation_id}"
raise errors.FatalError(msg)
assert build_python.exists(), (
f"{build_python.name} not found, has {list(build_python.parent.iterdir())}"
)
log.step(f"Installing Target Python {implementation_id}...")
target_install_path = install_target_cpython(tmp, python_configuration, free_threading)
target_python = (
target_install_path
/ "Python.xcframework"
/ python_configuration.xcframework_slice
/ "bin"
/ f"python{python_configuration.version}"
)
assert target_python.exists(), (
f"{target_python.name} not found, has {list(target_install_path.iterdir())}"
)
log.step("Creating cross build environment...")
venv_path = tmp / "venv"
env = cross_virtualenv(
py_version=python_configuration.version,
target_python=target_python,
multiarch=python_configuration.multiarch,
build_python=build_python,
venv_path=venv_path,
dependency_constraint=dependency_constraint,
xbuild_tools=xbuild_tools,
)
venv_bin_path = venv_path / "bin"
assert venv_bin_path.exists()
# We version pip ourselves, so we don't care about pip version checking
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
# upgrade pip to the version matching our constraints
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
pip = ["python", "-m", "pip"]
call(
*pip,
"install",
"--upgrade",
"pip",
*constraint_flags(dependency_constraint),
env=env,
cwd=venv_path,
)
# Apply our environment after pip is ready
env = environment.as_dictionary(prev_environment=env)
# Check what Python version we're on
which_python = call("which", "python", env=env, capture_stdout=True).strip()
print(which_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."
)
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 = (
"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)
# Ensure that IPHONEOS_DEPLOYMENT_TARGET is set in the environment
env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
log.step("Installing build tools...")
match build_frontend:
case "pip":
# No additional build tools required
pass
case "build":
call(
"pip",
"install",
"--upgrade",
"build[virtualenv]",
*constraint_flags(dependency_constraint),
env=env,
)
case _:
assert_never(build_frontend)
return target_install_path, env
def build(options: Options, tmp_path: Path) -> None:
if sys.platform != "darwin":
msg = "iOS binaries can only be built on macOS"
raise errors.FatalError(msg)
python_configurations = get_python_configurations(
build_selector=options.globals.build_selector,
architectures=options.globals.architectures,
)
if not python_configurations:
return
try:
before_all_options_identifier = python_configurations[0].identifier
before_all_options = options.build_options(before_all_options_identifier)
if before_all_options.before_all:
log.step("Running before_all...")
env = before_all_options.environment.as_dictionary(prev_environment=os.environ)
env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
before_all_prepared = prepare_command(
before_all_options.before_all,
project=".",
package=before_all_options.package_dir,
)
shell(before_all_prepared, env=env)
built_wheels: list[Path] = []
for config in python_configurations:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend
# uv doesn't support iOS
# Not using set because mypy can't narrow it
if build_frontend.name == "build[uv]" or build_frontend.name == "uv": # noqa: PLR1714
msg = "uv doesn't support iOS"
raise errors.FatalError(msg)
log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier
identifier_tmp_dir.mkdir()
built_wheel_dir = identifier_tmp_dir / "built_wheel"
repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel"
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version, tmp_dir=identifier_tmp_dir
)
target_install_path, env = setup_python(
identifier_tmp_dir / "build",
python_configuration=config,
dependency_constraint=constraints_path,
environment=build_options.environment,
build_frontend=build_frontend.name,
xbuild_tools=build_options.xbuild_tools,
)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
log.step_end()
print(
f"\nFound previously built wheel {compatible_wheel.name} "
f"that is compatible with {config.identifier}. "
"Skipping build step..."
)
test_wheel = compatible_wheel
else:
if build_options.before_build:
log.step("Running before_build...")
before_build_prepared = prepare_command(
build_options.before_build,
project=".",
package=build_options.package_dir,
)
shell(before_build_prepared, env=env)
log.step("Building wheel...")
built_wheel_dir.mkdir()
extra_flags = get_build_frontend_extra_flags(
build_frontend,
build_options.build_verbosity,
build_options.config_settings,
py38=False,
)
match build_frontend.name:
case "pip":
# Path.resolve() is needed. Without it pip wheel may try to
# fetch package from pypi.org. See
# https://github.com/pypa/cibuildwheel/pull/369
call(
"python",
"-m",
"pip",
"wheel",
build_options.package_dir.resolve(),
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
env=env,
)
case "build":
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=env,
)
case _:
assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl"))
if built_wheel.name.endswith("none-any.whl"):
raise errors.NonPlatformWheelError()
repaired_wheel_dir.mkdir()
if build_options.repair_command:
log.step("Repairing wheel...")
repair_command_prepared = prepare_command(
build_options.repair_command,
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
package=build_options.package_dir,
project=".",
)
shell(repair_command_prepared, env=env)
else:
shutil.move(str(built_wheel), repaired_wheel_dir)
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}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
test_wheel = repaired_wheel
log.step_end()
if build_options.test_command and build_options.test_selector(config.identifier):
if not config.is_simulator:
log.step("Skipping tests on non-simulator SDK")
elif config.arch != os.uname().machine:
log.step("Skipping tests on non-native simulator architecture")
else:
test_env = build_options.test_environment.as_dictionary(prev_environment=env)
if build_options.before_test:
before_test_prepared = prepare_command(
build_options.before_test,
project=".",
package=build_options.package_dir,
)
shell(before_test_prepared, env=test_env)
log.step("Setting up test harness...")
# Clone the testbed project into the build directory
testbed_path = identifier_tmp_dir / "testbed"
call(
"python",
target_install_path / "testbed",
"clone",
testbed_path,
env=test_env,
)
testbed_app_path = testbed_path / "iOSTestbed" / "app"
# Copy the test sources to the testbed app
if build_options.test_sources:
copy_test_sources(
build_options.test_sources,
Path.cwd(),
testbed_app_path,
)
else:
(testbed_app_path / "test_fail.py").write_text(
resources.TEST_FAIL_CWD_FILE.read_text()
)
log.step("Installing test requirements...")
# Install the compiled wheel (with any test extras), plus
# the test requirements. Use the --platform tag to force
# the installation of iOS wheels; this requires the use of
# --only-binary=:all:
ios_version = test_env["IPHONEOS_DEPLOYMENT_TARGET"]
platform_tag = f"ios_{ios_version.replace('.', '_')}_{config.arch}_{config.sdk}"
call(
"python",
"-m",
"pip",
"install",
"--only-binary=:all:",
"--platform",
platform_tag,
"--target",
testbed_path / "iOSTestbed" / "app_packages",
f"{test_wheel}{build_options.test_extras}",
*build_options.test_requires,
env=test_env,
)
log.step("Running test suite...")
# iOS doesn't support placeholders in the test command,
# because the source dir isn't visible on the simulator.
if (
"{project}" in build_options.test_command
or "{package}" in build_options.test_command
):
msg = unwrap_preserving_paragraphs(
f"""
iOS tests configured with a test command that uses the "{{project}}" or
"{{package}}" placeholder. iOS tests cannot use placeholders, because the
source directory is not visible on the simulator.
In addition, iOS tests must run as a Python module, so the test command
must begin with 'python -m'.
Test command: {build_options.test_command!r}
"""
)
raise errors.FatalError(msg)
test_command_list = shlex.split(build_options.test_command)
try:
for test_command_parts in split_command(test_command_list):
match test_command_parts:
case ["python", "-m", *rest]:
final_command = rest
case ["pytest", *rest]:
# pytest works exactly the same as a module, so we
# can just run it as a module.
msg = unwrap_preserving_paragraphs(f"""
iOS tests configured with a test command which doesn't start
with 'python -m'. iOS tests must execute python modules - other
entrypoints are not supported.
cibuildwheel will try to execute it as if it started with
'python -m'. If this works, all you need to do is add that to
your test command.
Test command: {build_options.test_command!r}
""")
log.warning(msg)
final_command = ["pytest", *rest]
case _:
msg = unwrap_preserving_paragraphs(
f"""
iOS tests configured with a test command which doesn't start
with 'python -m'. iOS tests must execute python modules - other
entrypoints are not supported.
Test command: {build_options.test_command!r}
"""
)
raise errors.FatalError(msg)
test_runtime_args = build_options.test_runtime.args
# 2025-10: The GitHub Actions macos-15 runner has a known issue where
# the default simulator won't start due to a disk performance issue;
# see https://github.com/actions/runner-images/issues/12777 for details.
# In the meantime, if it looks like we're running on a GitHub Actions
# macos-15 runner, use a simulator that is known to work, unless the
# user explicitly specifies a simulator.
os_version, _, arch = platform.mac_ver()
if (
"GITHUB_ACTIONS" in os.environ
and os_version.startswith("15.")
and arch == "arm64"
and not any(
arg.startswith("--simulator") for arg in test_runtime_args
)
):
test_runtime_args = [
"--simulator",
"iPhone 16e,OS=18.5",
*test_runtime_args,
]
call(
"python",
testbed_path,
"run",
*(["--verbose"] if build_options.build_verbosity > 0 else []),
*test_runtime_args,
"--",
*final_command,
env=test_env,
)
except subprocess.CalledProcessError:
# catches the first test command failure in the loop,
# implementing short-circuiting
log.step_end(success=False)
log.error(f"Test suite failed on {config.identifier}")
sys.exit(1)
log.step_end()
# We're all done here; move it to output (overwrite existing)
output_wheel: Path | None = None
if compatible_wheel is None:
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
log.warning(
f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
# Clean up
shutil.rmtree(identifier_tmp_dir)
log.build_end(output_wheel)
except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
@@ -1,35 +1,40 @@
from __future__ import annotations
import contextlib
import dataclasses
import subprocess
import sys
import textwrap
from collections import OrderedDict
from collections.abc import Iterable, Iterator, Sequence, Set
from dataclasses import dataclass
from pathlib import Path, PurePath, PurePosixPath
from typing import OrderedDict, Tuple
from typing import TYPE_CHECKING, assert_never
from ._compat.typing import assert_never
from .architecture import Architecture
from .logger import log
from .oci_container import OCIContainer, OCIContainerEngineConfig
from .options import BuildOptions, Options
from .typing import PathOrStr
from .util import (
AlreadyBuiltWheelError,
BuildFrontendConfig,
BuildSelector,
NonPlatformWheelError,
find_compatible_wheel,
get_build_verbosity_extra_flags,
prepare_command,
read_python_configs,
split_config_settings,
test_fail_cwd_file,
unwrap,
)
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture
from cibuildwheel.frontend import get_build_frontend_extra_flags
from cibuildwheel.logger import log
from cibuildwheel.oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
from cibuildwheel.options import BuildOptions, Options
from cibuildwheel.selector import BuildSelector
from cibuildwheel.util import resources
from cibuildwheel.util.file import copy_test_sources
from cibuildwheel.util.helpers import prepare_command, unwrap
from cibuildwheel.util.packaging import find_compatible_wheel
if TYPE_CHECKING:
from cibuildwheel.typing import PathOrStr
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,
Architecture.riscv64: OCIPlatform.RISCV64,
}
@dataclass(frozen=True)
@dataclasses.dataclass(frozen=True, kw_only=True)
class PythonConfiguration:
version: str
identifier: str
@@ -40,7 +45,7 @@ class PythonConfiguration:
return PurePosixPath(self.path_str)
@dataclass(frozen=True)
@dataclasses.dataclass(frozen=True, kw_only=True)
class BuildStep:
platform_configs: list[PythonConfiguration]
platform_tag: str
@@ -48,13 +53,16 @@ class BuildStep:
container_image: str
def all_python_configurations() -> list[PythonConfiguration]:
config_dicts = resources.read_python_configs("linux")
return [PythonConfiguration(**item) for item in config_dicts]
def get_python_configurations(
build_selector: BuildSelector,
architectures: Set[Architecture],
) -> list[PythonConfiguration]:
full_python_configs = read_python_configs("linux")
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
python_configurations = all_python_configurations()
# return all configurations whose arch is in our `architectures` set,
# and match the build/skip rules
@@ -93,7 +101,7 @@ def get_build_steps(
Groups PythonConfigurations into BuildSteps. Each BuildStep represents a
separate container instance.
"""
steps = OrderedDict[Tuple[str, str, str, OCIContainerEngineConfig], BuildStep]()
steps = OrderedDict[tuple[str, str, str, OCIContainerEngineConfig], BuildStep]()
for config in python_configurations:
_, platform_tag = config.identifier.split("-", 1)
@@ -123,33 +131,36 @@ def check_all_python_exist(
*, platform_configs: Iterable[PythonConfiguration], container: OCIContainer
) -> None:
exist = True
has_manylinux_interpreters = True
has_manylinux_interpreters = False
messages = []
try:
with contextlib.suppress(subprocess.CalledProcessError):
# use capture_output to keep quiet
container.call(["manylinux-interpreters", "--help"], capture_output=True)
except subprocess.CalledProcessError:
has_manylinux_interpreters = False
has_manylinux_interpreters = True
for config in platform_configs:
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(["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
except subprocess.CalledProcessError:
messages.append(
f" 'manylinux-interpreters ensure {config.path.name}' needed to build '{config.identifier}' failed in container running image '{container.image}'."
" Either the installation failed or this interpreter is not available in that image. Please check the logs."
)
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:
message = "\n".join(messages)
print(
f"cibuildwheel:\n{message}",
file=sys.stderr,
)
sys.exit(1)
raise errors.FatalError(message)
def build_in_container(
@@ -159,6 +170,7 @@ def build_in_container(
container: OCIContainer,
container_project_path: PurePath,
container_package_dir: PurePath,
local_tmp_dir: Path,
) -> None:
container_output_dir = PurePosixPath("/output")
@@ -174,7 +186,7 @@ def build_in_container(
log.step("Running before_all...")
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_ROOT_USER_ACTION"] = "ignore"
env = before_all_options.environment.as_dictionary(
@@ -192,48 +204,50 @@ def build_in_container(
for config in platform_configs:
log.build_start(config.identifier)
local_identifier_tmp_dir = local_tmp_dir / config.identifier
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
dependency_constraint_flags: list[PathOrStr] = []
if build_options.dependency_constraints:
constraints_file = build_options.dependency_constraints.get_for_python_version(
config.version
)
container_constraints_file = PurePosixPath("/constraints.txt")
container.copy_into(constraints_file, container_constraints_file)
dependency_constraint_flags = ["-c", container_constraints_file]
build_frontend = build_options.build_frontend
use_uv = build_frontend.name in {"build[uv]", "uv"}
pip = ["uv", "pip"] if use_uv else ["pip"]
log.step("Setting up build environment...")
dependency_constraint_flags: list[PathOrStr] = []
local_constraints_file = build_options.dependency_constraints.get_for_python_version(
version=config.version,
tmp_dir=local_identifier_tmp_dir,
)
if local_constraints_file:
container_constraints_file = PurePosixPath("/constraints.txt")
container.copy_into(local_constraints_file, container_constraints_file)
dependency_constraint_flags = ["-c", container_constraints_file]
env = container.get_environment()
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
env["PIP_ROOT_USER_ACTION"] = "ignore"
# put this config's python top of the list
python_bin = config.path / "bin"
env["PATH"] = f'{python_bin}:{env["PATH"]}'
env["PATH"] = f"{python_bin}:{env['PATH']}"
env = build_options.environment.as_dictionary(env, executor=container.environment_executor)
# check config python is still on PATH
which_python = container.call(["which", "python"], env=env, capture_output=True).strip()
if PurePosixPath(which_python) != python_bin / "python":
print(
"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.",
file=sys.stderr,
)
sys.exit(1)
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)
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
if PurePosixPath(which_pip) != python_bin / "pip":
print(
"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.",
file=sys.stderr,
)
sys.exit(1)
if use_uv:
which_uv = container.call(["which", "uv"], env=env, capture_output=True).strip()
if not which_uv:
msg = "uv not found on PATH. You must use a supported manylinux or musllinux environment with uv."
raise errors.FatalError(msg)
else:
which_pip = container.call(["which", "pip"], env=env, capture_output=True).strip()
if PurePosixPath(which_pip) != python_bin / "pip":
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)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
@@ -241,7 +255,7 @@ def build_in_container(
print(
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
)
repaired_wheels = [compatible_wheel]
repaired_wheel = compatible_wheel
else:
if build_options.before_build:
log.step("Running before_build...")
@@ -259,42 +273,58 @@ def build_in_container(
container.call(["rm", "-rf", built_wheel_dir])
container.call(["mkdir", "-p", built_wheel_dir])
extra_flags = split_config_settings(build_options.config_settings, build_frontend.name)
extra_flags += build_frontend.args
extra_flags = get_build_frontend_extra_flags(
build_frontend,
build_options.build_verbosity,
build_options.config_settings,
py38=config.identifier[1:].startswith("p38"),
)
if build_frontend.name == "pip":
extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
container.call(
[
"python",
"-m",
"pip",
"wheel",
container_package_dir,
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
],
env=env,
)
elif build_frontend.name == "build":
if not 0 <= build_options.build_verbosity < 2:
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
log.warning(msg)
container.call(
[
"python",
"-m",
"build",
container_package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
],
env=env,
)
else:
assert_never(build_frontend)
match build_frontend.name:
case "pip":
container.call(
[
"python",
"-m",
"pip",
"wheel",
container_package_dir,
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
],
env=env,
)
case "build" | "build[uv]":
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
extra_flags += ["--installer=uv"]
container.call(
[
"python",
"-m",
"build",
container_package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
],
env=env,
)
case "uv":
container.call(
[
"uv",
"build",
f"--python={python_bin / 'python'}",
container_package_dir,
"--wheel",
f"--out-dir={built_wheel_dir}",
*extra_flags,
],
env=env,
)
case _:
assert_never(build_frontend)
built_wheel = container.glob(built_wheel_dir, "*.whl")[0]
@@ -303,43 +333,62 @@ def build_in_container(
container.call(["mkdir", "-p", repaired_wheel_dir])
if built_wheel.name.endswith("none-any.whl"):
raise NonPlatformWheelError()
raise errors.NonPlatformWheelError()
if build_options.repair_command:
log.step("Repairing wheel...")
repair_command_prepared = prepare_command(
build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
build_options.repair_command,
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
package=container_package_dir,
project=container_project_path,
)
container.call(["sh", "-c", repair_command_prepared], env=env)
else:
container.call(["mv", built_wheel, repaired_wheel_dir])
repaired_wheels = container.glob(repaired_wheel_dir, "*.whl")
match container.glob(repaired_wheel_dir, "*.whl"):
case []:
raise errors.RepairStepProducedNoWheelError()
case [repaired_wheel]:
pass
case too_many:
raise errors.RepairStepProducedMultipleWheelsError([p.name for p in too_many])
for repaired_wheel in repaired_wheels:
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise AlreadyBuiltWheelError(repaired_wheel.name)
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
if build_options.test_command and build_options.test_selector(config.identifier):
log.step("Testing wheel...")
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
container.call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
if not use_uv:
container.call(
["pip", "install", "virtualenv", *dependency_constraint_flags], env=env
)
testing_temp_dir = PurePosixPath(
container.call(["mktemp", "-d"], capture_output=True).strip()
)
venv_dir = testing_temp_dir / "venv"
container.call(["python", "-m", "virtualenv", "--no-download", venv_dir], env=env)
if use_uv:
container.call(["uv", "venv", venv_dir, "--python", python_bin / "python"], env=env)
else:
# Use embedded dependencies from virtualenv to ensure determinism
venv_args = ["--no-periodic-update", "--pip=embed", "--no-setuptools"]
if "38" in config.identifier:
venv_args.append("--no-wheel")
container.call(["python", "-m", "virtualenv", *venv_args, venv_dir], env=env)
virtualenv_env = env.copy()
virtualenv_env["PATH"] = f"{venv_dir / 'bin'}:{virtualenv_env['PATH']}"
# TODO remove me once virtualenv provides pip>=24.1b1
if config.version == "3.13":
container.call(["pip", "install", "pip>=24.1b1"], env=virtualenv_env)
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
virtualenv_env = build_options.test_environment.as_dictionary(
prev_environment=virtualenv_env
)
if build_options.before_test:
before_test_prepared = prepare_command(
@@ -350,46 +399,52 @@ def build_in_container(
container.call(["sh", "-c", before_test_prepared], env=virtualenv_env)
# Install the wheel we just built
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
# conforms to multiple manylinux standards. These multiple versions of the wheel are
# functionally the same, differing only in name, wheel metadata, and possibly include
# different external shared libraries. so it doesn't matter which one we run the tests on.
# Let's just pick the first one.
wheel_to_test = repaired_wheels[0]
container.call(
["pip", "install", str(wheel_to_test) + build_options.test_extras],
[*pip, "install", str(repaired_wheel) + build_options.test_extras],
env=virtualenv_env,
)
# Install any requirements to run the tests
if build_options.test_requires:
container.call(["pip", "install", *build_options.test_requires], env=virtualenv_env)
container.call([*pip, "install", *build_options.test_requires], env=virtualenv_env)
# Run the tests from a different directory
test_command_prepared = prepare_command(
build_options.test_command,
project=container_project_path,
package=container_package_dir,
wheel=wheel_to_test,
wheel=repaired_wheel,
)
test_cwd = testing_temp_dir / "test_cwd"
container.call(["mkdir", "-p", test_cwd])
container.copy_into(test_fail_cwd_file, test_cwd / "test_fail.py")
if build_options.test_sources:
copy_test_sources(
build_options.test_sources,
Path.cwd(),
test_cwd,
copy_into=container.copy_into,
)
else:
# Use the test_fail.py file to raise a nice error if the user
# tries to run tests in the cwd
container.copy_into(resources.TEST_FAIL_CWD_FILE, test_cwd / "test_fail.py")
container.call(["sh", "-c", test_command_prepared], cwd=test_cwd, env=virtualenv_env)
# clean up test environment
container.call(["rm", "-rf", testing_temp_dir])
# move repaired wheels to output
# move repaired wheel to output
output_wheel: Path | None = None
if compatible_wheel is None:
container.call(["mkdir", "-p", container_output_dir])
container.call(["mv", *repaired_wheels, container_output_dir])
built_wheels.extend(
container_output_dir / repaired_wheel.name for repaired_wheel in repaired_wheels
)
container.call(["mv", repaired_wheel, container_output_dir])
built_wheels.append(container_output_dir / repaired_wheel.name)
output_wheel = options.globals.output_dir / repaired_wheel.name
log.build_end()
log.build_end(output_wheel)
log.step("Copying wheels back to host...")
# copy the output back into the host
@@ -397,7 +452,7 @@ def build_in_container(
log.step_end()
def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
def build(options: Options, tmp_path: Path) -> None:
python_configurations = get_python_configurations(
options.globals.build_selector, options.globals.architectures
)
@@ -419,31 +474,29 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
check=True,
stdout=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
print(
unwrap(
f"""
cibuildwheel: {build_step.container_engine.name} not found. An
OCI exe like Docker or Podman is required to run Linux builds.
If you're building on Travis CI, add `services: [docker]` to
your .travis.yml. If you're building on Circle CI in Linux,
add a `setup_remote_docker` step to your .circleci/config.yml.
If you're building on Cirrus CI, use `docker_builder` task.
"""
),
file=sys.stderr,
except subprocess.CalledProcessError as error:
msg = unwrap(
f"""
{build_step.container_engine.name} not found. An OCI exe like
Docker or Podman is required to run Linux builds. If you're
building on Travis CI, add `services: [docker]` to your
.travis.yml. If you're building on Circle CI in Linux, add a
`setup_remote_docker` step to your .circleci/config.yml. If
you're building on Cirrus CI, use `docker_builder` task.
"""
)
sys.exit(2)
raise errors.ConfigurationError(msg) from error
try:
ids_to_build = [x.identifier for x in build_step.platform_configs]
log.step(f"Starting container image {build_step.container_image}...")
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(
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,
engine=build_step.container_engine,
) as container:
@@ -453,14 +506,13 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
container=container,
container_project_path=container_project_path,
container_package_dir=container_package_dir,
local_tmp_dir=tmp_path,
)
except subprocess.CalledProcessError as error:
log.step_end_with_error(
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
)
troubleshoot(options, error)
sys.exit(1)
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
def _matches_prepared_command(error_cmd: Sequence[str], command_template: str) -> bool:
@@ -473,13 +525,13 @@ def _matches_prepared_command(error_cmd: Sequence[str], command_template: str) -
def troubleshoot(options: Options, error: Exception) -> None:
if isinstance(error, subprocess.CalledProcessError) and (
error.cmd[0:4] == ["python", "-m", "pip", "wheel"]
or error.cmd[0:2] == ["uv", "build"]
or error.cmd[0:3] == ["python", "-m", "build"]
or _matches_prepared_command(
error.cmd, options.build_options(None).repair_command
) # TODO allow matching of overrides too?
):
# the wheel build step or the repair step failed
print("Checking for common errors...")
so_files = list(options.globals.package_dir.glob("**/*.so"))
if so_files:
@@ -1,7 +1,6 @@
from __future__ import annotations
import contextlib
import dataclasses
import functools
import inspect
import os
import platform
import re
@@ -9,43 +8,30 @@ import shutil
import subprocess
import sys
import typing
from collections.abc import Sequence, Set
from dataclasses import dataclass
from collections.abc import Set
from pathlib import Path
from typing import Literal, Tuple
from typing import Literal, assert_never
from filelock import FileLock
from packaging.version import Version
from ._compat.typing import assert_never
from .architecture import Architecture
from .environment import ParsedEnvironment
from .logger import log
from .options import Options
from .typing import PathOrStr
from .util import (
CIBW_CACHE_PATH,
AlreadyBuiltWheelError,
BuildFrontendConfig,
BuildFrontendName,
BuildSelector,
NonPlatformWheelError,
call,
detect_ci_provider,
download,
find_compatible_wheel,
get_build_verbosity_extra_flags,
get_pip_version,
install_certifi_script,
prepare_command,
read_python_configs,
shell,
split_config_settings,
test_fail_cwd_file,
unwrap,
virtualenv,
)
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture
from cibuildwheel.ci import detect_ci_provider
from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.frontend import BuildFrontendName, get_build_frontend_extra_flags
from cibuildwheel.logger import log
from cibuildwheel.options import Options
from cibuildwheel.selector import BuildSelector
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call, shell
from cibuildwheel.util.file import CIBW_CACHE_PATH, copy_test_sources, download, move_file
from cibuildwheel.util.helpers import prepare_command, unwrap
from cibuildwheel.util.packaging import find_compatible_wheel, get_pip_version
from cibuildwheel.venv import constraint_flags, find_uv, virtualenv
@functools.cache
def get_macos_version() -> tuple[int, int]:
"""
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
@@ -57,7 +43,27 @@ def get_macos_version() -> tuple[int, int]:
"""
version_str, _, _ = platform.mac_ver()
version = tuple(map(int, version_str.split(".")[:2]))
return typing.cast(Tuple[int, int], version)
if (10, 15) < version < (11, 0):
# When built against an older macOS SDK, Python will report macOS 10.16
# instead of the real version.
version_str = call(
sys.executable,
"-sS",
"-c",
"import platform; print(platform.mac_ver()[0])",
env={"SYSTEM_VERSION_COMPAT": "0"},
capture_stdout=True,
)
version = tuple(map(int, version_str.split(".")[:2]))
return typing.cast("tuple[int, int]", version)
@functools.cache
def get_test_macosx_deployment_target() -> str:
version = get_macos_version()
if version >= (11, 0):
return f"{version[0]}.0"
return f"{version[0]}.{version[1]}"
def get_macos_sdks() -> list[str]:
@@ -65,19 +71,22 @@ def get_macos_sdks() -> list[str]:
return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)]
@dataclass(frozen=True)
@dataclasses.dataclass(frozen=True, kw_only=True)
class PythonConfiguration:
version: str
identifier: str
url: str
def all_python_configurations() -> list[PythonConfiguration]:
config_dicts = resources.read_python_configs("macos")
return [PythonConfiguration(**item) for item in config_dicts]
def get_python_configurations(
build_selector: BuildSelector, architectures: Set[Architecture]
) -> list[PythonConfiguration]:
full_python_configs = read_python_configs("macos")
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
python_configurations = all_python_configurations()
# filter out configs that don't match any of the selected architectures
python_configurations = [
@@ -89,7 +98,7 @@ def get_python_configurations(
# skip builds as required by BUILD/SKIP
python_configurations = [c for c in python_configurations if build_selector(c.identifier)]
# filter-out some cross-compilation configs with PyPy:
# filter-out some cross-compilation configs with PyPy and GraalPy:
# can't build arm64 on x86_64
# rosetta allows to build x86_64 on arm64
if platform.machine() == "x86_64":
@@ -97,7 +106,7 @@ def get_python_configurations(
python_configurations = [
c
for c in python_configurations
if not (c.identifier.startswith("pp") and c.identifier.endswith("arm64"))
if not (c.identifier.startswith(("pp", "gp")) and c.identifier.endswith("arm64"))
]
removed_elements = python_configurations_before - set(python_configurations)
if removed_elements:
@@ -105,7 +114,7 @@ def get_python_configurations(
log.quiet(
unwrap(
f"""
Note: {ids} {'was' if len(removed_elements) == 1 else 'were'}
Note: {ids} {"was" if len(removed_elements) == 1 else "were"}
selected, but can't be built on x86_64 so will be skipped automatically.
"""
)
@@ -114,35 +123,55 @@ def get_python_configurations(
return python_configurations
def install_cpython(tmp: Path, version: str, url: str) -> Path:
installation_path = Path(f"/Library/Frameworks/Python.framework/Versions/{version}")
def install_cpython(_tmp: Path, version: str, url: str, free_threading: bool) -> Path:
ft = "T" if free_threading else ""
installation_path = Path(f"/Library/Frameworks/Python{ft}.framework/Versions/{version}")
with FileLock(CIBW_CACHE_PATH / f"cpython{version}.lock"):
installed_system_packages = call("pkgutil", "--pkgs", capture_stdout=True).splitlines()
# if this version of python isn't installed, get it from python.org and install
python_package_identifier = f"org.python.Python.PythonFramework-{version}"
python_package_identifier = f"org.python.Python.Python{ft}Framework-{version}"
if python_package_identifier not in installed_system_packages:
if detect_ci_provider() is None:
# if running locally, we don't want to install CPython with sudo
# let the user know & provide a link to the installer
print(
f"Error: CPython {version} is not installed.\n"
"cibuildwheel will not perform system-wide installs when running outside of CI.\n"
f"To build locally, install CPython {version} on this machine, or, disable this version of Python using CIBW_SKIP=cp{version.replace('.', '')}-macosx_*\n"
f"\nDownload link: {url}",
file=sys.stderr,
msg = inspect.cleandoc(
f"""
Framework CPython {version} not detected as installed in:
{installation_path}
cibuildwheel will not perform system-wide installs when running outside of CI.
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 SystemExit(1)
pkg_path = tmp / "Python.pkg"
# download the pkg
download(url, pkg_path)
# install
call("sudo", "installer", "-pkg", pkg_path, "-target", "/")
raise errors.FatalError(msg)
python_filename = url.rsplit("/", maxsplit=1)[-1]
pkg_path = CIBW_CACHE_PATH / "cpython-installer" / python_filename
if not pkg_path.exists():
download(url, pkg_path)
args = []
if version.startswith("3.13"):
# Python 3.13 is the first version to have a free-threading option
args += ["-applyChoiceChangesXML", str(resources.FREE_THREAD_ENABLE_313.resolve())]
elif version.startswith("3.14"):
args += ["-applyChoiceChangesXML", str(resources.FREE_THREAD_ENABLE_314.resolve())]
call("sudo", "installer", "-pkg", pkg_path, *args, "-target", "/")
pkg_path.unlink()
env = os.environ.copy()
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
call(installation_path / "bin" / "python3", install_certifi_script, env=env)
return installation_path / "bin" / "python3"
if free_threading:
call(installation_path / f"bin/python{version}t", "-m", "ensurepip", env=env)
call(
installation_path / f"bin/python{version}t",
resources.INSTALL_CERTIFI_SCRIPT,
env=env,
)
else:
call(installation_path / "bin/python3", resources.INSTALL_CERTIFI_SCRIPT, env=env)
return installation_path / "bin" / (f"python{version}t" if free_threading else "python3")
def install_pypy(tmp: Path, url: str) -> Path:
@@ -160,29 +189,60 @@ def install_pypy(tmp: Path, url: str) -> Path:
return installation_path / "bin" / "pypy3"
def install_graalpy(tmp: Path, url: str) -> Path:
graalpy_archive = url.rsplit("/", 1)[-1]
extension = ".tar.gz"
assert graalpy_archive.endswith(extension)
installation_path = CIBW_CACHE_PATH / graalpy_archive[: -len(extension)]
with FileLock(str(installation_path) + ".lock"):
if not installation_path.exists():
downloaded_archive = tmp / graalpy_archive
download(url, downloaded_archive)
installation_path.mkdir(parents=True)
# GraalPy top-folder name is inconsistent with archive name
call("tar", "-C", installation_path, "--strip-components=1", "-xzf", downloaded_archive)
downloaded_archive.unlink()
return installation_path / "bin" / "graalpy"
def setup_python(
tmp: Path,
python_configuration: PythonConfiguration,
dependency_constraint_flags: Sequence[PathOrStr],
dependency_constraint: Path | None,
environment: ParsedEnvironment,
build_frontend: BuildFrontendName,
) -> dict[str, str]:
) -> tuple[Path, dict[str, str]]:
uv_path = find_uv()
use_uv = build_frontend in {"build[uv]", "uv"}
tmp.mkdir()
implementation_id = python_configuration.identifier.split("-")[0]
log.step(f"Installing Python {implementation_id}...")
if implementation_id.startswith("cp"):
base_python = install_cpython(tmp, python_configuration.version, python_configuration.url)
free_threading = "t-macos" in python_configuration.identifier
base_python = install_cpython(
tmp, python_configuration.version, python_configuration.url, free_threading
)
elif implementation_id.startswith("pp"):
base_python = install_pypy(tmp, python_configuration.url)
elif implementation_id.startswith("gp"):
base_python = install_graalpy(tmp, python_configuration.url)
else:
msg = "Unknown Python implementation"
raise ValueError(msg)
assert base_python.exists()
assert base_python.exists(), (
f"{base_python.name} not found, has {list(base_python.parent.iterdir())}"
)
log.step("Setting up build environment...")
venv_path = tmp / "venv"
env = virtualenv(
python_configuration.version, base_python, venv_path, dependency_constraint_flags
python_configuration.version,
base_python,
venv_path,
dependency_constraint,
use_uv=use_uv,
)
venv_bin_path = venv_path / "bin"
assert venv_bin_path.exists()
@@ -193,49 +253,34 @@ def setup_python(
# https://github.com/pypa/virtualenv/issues/620
# Also see https://github.com/python/cpython/pull/9516
env.pop("__PYVENV_LAUNCHER__", None)
# uv uses this over the current environment's python, so remove it to avoid confusion
env.pop("PYTHON_VERSION", None)
env.pop("PYTHON_ARCH", None)
env.pop("UV_PYTHON", None)
# we version pip ourselves, so we don't care about pip version checking
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
# upgrade pip to the version matching our constraints
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
call(
"python",
"-m",
"pip",
"install",
"--upgrade",
"pip",
*dependency_constraint_flags,
env=env,
cwd=venv_path,
)
# Apply our environment after pip is ready
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"):
print(
"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.",
file=sys.stderr,
)
sys.exit(1)
# 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()
print(which_python)
if which_python != str(venv_bin_path / "python"):
print(
"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.",
file=sys.stderr,
)
sys.exit(1)
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)
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 = "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_universal2 = python_configuration.identifier.endswith("universal2")
@@ -243,23 +288,42 @@ def setup_python(
# Set MACOSX_DEPLOYMENT_TARGET, if the user didn't set it.
# For arm64, the minimal deployment target is 11.0.
# On x86_64 (or universal2), use 10.9 as a default.
# PyPy defaults to 10.7, causing inconsistencies if it's left unset.
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "11.0" if config_is_arm64 else "10.9")
# CPython 3.12.6+ needs 10.13.
# CPython 3.14.0 needs 10.15.
if config_is_arm64:
default_target = "11.0"
elif Version(python_configuration.version) >= Version("3.14"):
default_target = "10.15"
elif Version(python_configuration.version) >= Version("3.12"):
default_target = "10.13"
elif python_configuration.identifier.startswith("pp") and Version(
python_configuration.version
) >= Version("3.9"):
default_target = "10.15"
else:
default_target = "10.9"
env.setdefault("MACOSX_DEPLOYMENT_TARGET", default_target)
if python_configuration.version not in {"3.6", "3.7"}:
if config_is_arm64:
# macOS 11 is the first OS with arm64 support, so the wheels
# have that as a minimum.
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-11.0-arm64")
env.setdefault("ARCHFLAGS", "-arch arm64")
elif config_is_universal2:
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-10.9-universal2")
env.setdefault("ARCHFLAGS", "-arch arm64 -arch x86_64")
elif python_configuration.identifier.endswith("x86_64"):
# even on the macos11.0 Python installer, on the x86_64 side it's
# compatible back to 10.9.
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-10.9-x86_64")
env.setdefault("ARCHFLAGS", "-arch x86_64")
# This is a floor, it can't be set lower than the default_target.
if Version(env["MACOSX_DEPLOYMENT_TARGET"]) < Version(default_target):
log.warning(
f"Bumping MACOSX_DEPLOYMENT_TARGET ({env['MACOSX_DEPLOYMENT_TARGET']}) to the minimum required ({default_target})."
)
env["MACOSX_DEPLOYMENT_TARGET"] = default_target
if config_is_arm64:
# macOS 11 is the first OS with arm64 support, so the wheels
# have that as a minimum.
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-11.0-arm64")
env.setdefault("ARCHFLAGS", "-arch arm64")
elif config_is_universal2:
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-10.9-universal2")
env.setdefault("ARCHFLAGS", "-arch arm64 -arch x86_64")
elif python_configuration.identifier.endswith("x86_64"):
# even on the macos11.0 Python installer, on the x86_64 side it's
# compatible back to 10.9.
env.setdefault("_PYTHON_HOST_PLATFORM", "macosx-10.9-x86_64")
env.setdefault("ARCHFLAGS", "-arch x86_64")
building_arm64 = config_is_arm64 or config_is_universal2
if building_arm64 and get_macos_version() < (10, 16) and "SDKROOT" not in env:
@@ -284,29 +348,57 @@ def setup_python(
env.setdefault("SDKROOT", arm64_compatible_sdks[0])
log.step("Installing build tools...")
if build_frontend == "pip":
call(
"pip",
"install",
"--upgrade",
"delocate",
*dependency_constraint_flags,
env=env,
)
elif build_frontend == "build":
call(
"pip",
"install",
"--upgrade",
"delocate",
"build[virtualenv]",
*dependency_constraint_flags,
env=env,
)
else:
assert_never(build_frontend)
match build_frontend:
case "pip":
call(
"pip",
"install",
"--upgrade",
"delocate",
*constraint_flags(dependency_constraint),
env=env,
)
case "build":
call(
"pip",
"install",
"--upgrade",
"delocate",
"build[virtualenv]",
*constraint_flags(dependency_constraint),
env=env,
)
case "build[uv]":
assert uv_path is not None
call(
uv_path,
"pip",
"install",
"--python",
which_python,
"--upgrade",
"delocate",
"build[virtualenv, uv]",
*constraint_flags(dependency_constraint),
env=env,
)
case "uv":
assert uv_path is not None
call(
uv_path,
"pip",
"install",
"--python",
which_python,
"--upgrade",
"delocate",
*constraint_flags(dependency_constraint),
env=env,
)
case _:
assert_never(build_frontend)
return env
return base_python, env
def build(options: Options, tmp_path: Path) -> None:
@@ -334,7 +426,13 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
build_frontend = build_options.build_frontend
use_uv = build_frontend.name in {"build[uv]", "uv"}
uv_path = find_uv()
if use_uv and uv_path is None:
msg = "uv not found"
raise AssertionError(msg)
pip = ["pip"] if not use_uv else [str(uv_path), "pip"]
log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier
@@ -345,20 +443,18 @@ def build(options: Options, tmp_path: Path) -> None:
config_is_arm64 = config.identifier.endswith("arm64")
config_is_universal2 = config.identifier.endswith("universal2")
dependency_constraint_flags: Sequence[PathOrStr] = []
if build_options.dependency_constraints:
dependency_constraint_flags = [
"-c",
build_options.dependency_constraints.get_for_python_version(config.version),
]
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version, tmp_dir=identifier_tmp_dir
)
env = setup_python(
base_python, env = setup_python(
identifier_tmp_dir / "build",
config,
dependency_constraint_flags,
constraints_path,
build_options.environment,
build_frontend.name,
)
pip_version = None if use_uv else get_pip_version(env)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
@@ -378,61 +474,68 @@ def build(options: Options, tmp_path: Path) -> None:
log.step("Building wheel...")
built_wheel_dir.mkdir()
extra_flags = split_config_settings(
build_options.config_settings, build_frontend.name
extra_flags = get_build_frontend_extra_flags(
build_frontend,
build_options.build_verbosity,
build_options.config_settings,
py38=config.identifier[1:].startswith("p38"),
)
extra_flags += build_frontend.args
build_env = env.copy()
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
if build_options.dependency_constraints:
constraint_path = build_options.dependency_constraints.get_for_python_version(
config.version
)
user_constraints = build_env.get("PIP_CONSTRAINT")
our_constraints = constraint_path.as_uri()
build_env["PIP_CONSTRAINT"] = " ".join(
c for c in [user_constraints, our_constraints] if c
)
if build_frontend.name == "pip":
extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369
call(
"python",
"-m",
"pip",
"wheel",
build_options.package_dir.resolve(),
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
env=build_env,
)
elif build_frontend.name == "build":
if not 0 <= build_options.build_verbosity < 2:
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
log.warning(msg)
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
else:
assert_never(build_frontend)
match build_frontend.name:
case "pip":
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369
call(
"python",
"-m",
"pip",
"wheel",
build_options.package_dir.resolve(),
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
env=build_env,
)
case "build" | "build[uv]":
if (
use_uv
and "--no-isolation" not in extra_flags
and "-n" not in extra_flags
):
extra_flags.append("--installer=uv")
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
case "uv":
assert uv_path is not None
call(
uv_path,
"build",
f"--python={base_python}",
build_options.package_dir,
"--wheel",
f"--out-dir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
case _:
assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl"))
repaired_wheel_dir.mkdir()
if built_wheel.name.endswith("none-any.whl"):
raise NonPlatformWheelError()
raise errors.NonPlatformWheelError()
if build_options.repair_command:
log.step("Repairing wheel...")
@@ -449,15 +552,20 @@ def build(options: Options, tmp_path: Path) -> None:
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
delocate_archs=delocate_archs,
package=build_options.package_dir,
project=".",
)
shell(repair_command_prepared, env=env)
else:
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}:
raise AlreadyBuiltWheelError(repaired_wheel.name)
raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
log.step_end()
@@ -541,17 +649,13 @@ def build(options: Options, tmp_path: Path) -> None:
else f"Testing wheel on {testing_arch}..."
)
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
arch_prefix = []
uv_arch_args = []
if testing_arch != machine_arch:
if machine_arch == "arm64" and testing_arch == "x86_64":
# rosetta2 will provide the emulation with just the arch prefix.
arch_prefix = ["arch", "-x86_64"]
uv_arch_args = ["--python-platform", "x86_64-apple-darwin"]
else:
msg = f"don't know how to emulate {testing_arch} on {machine_arch}"
raise RuntimeError(msg)
@@ -560,25 +664,32 @@ def build(options: Options, tmp_path: Path) -> None:
call_with_arch = functools.partial(call, *arch_prefix)
shell_with_arch = functools.partial(call, *arch_prefix, "/bin/sh", "-c")
# Use --no-download to ensure determinism by using seed libraries
# built into virtualenv
call_with_arch("python", "-m", "virtualenv", "--no-download", venv_dir, env=env)
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
virtualenv_env = virtualenv(
config.version,
base_python,
venv_dir,
None,
use_uv=use_uv,
env=env,
pip_version=pip_version,
)
if use_uv:
pip_install = functools.partial(call, *pip, "install", *uv_arch_args)
else:
pip_install = functools.partial(call_with_arch, *pip, "install")
virtualenv_env = env.copy()
virtualenv_env["PATH"] = os.pathsep.join(
[
str(venv_dir / "bin"),
virtualenv_env["PATH"],
]
virtualenv_env["MACOSX_DEPLOYMENT_TARGET"] = get_test_macosx_deployment_target()
virtualenv_env = build_options.test_environment.as_dictionary(
prev_environment=virtualenv_env
)
# check that we are using the Python from the virtual environment
call_with_arch("which", "python", env=virtualenv_env)
# TODO remove me once virtualenv provides pip>=24.1b1
if config.version == "3.13":
call("python", "-m", "pip", "install", "pip>=24.1b1", env=virtualenv_env)
if build_options.before_test:
before_test_prepared = prepare_command(
build_options.before_test,
@@ -605,18 +716,14 @@ def build(options: Options, tmp_path: Path) -> None:
else:
virtualenv_env_install_wheel = virtualenv_env
call_with_arch(
"pip",
"install",
pip_install(
f"{repaired_wheel}{build_options.test_extras}",
env=virtualenv_env_install_wheel,
)
# test the wheel
if build_options.test_requires:
call_with_arch(
"pip",
"install",
pip_install(
*build_options.test_requires,
env=virtualenv_env_install_wheel,
)
@@ -626,31 +733,48 @@ def build(options: Options, tmp_path: Path) -> None:
# and not the repo code)
test_command_prepared = prepare_command(
build_options.test_command,
project=Path(".").resolve(),
project=Path.cwd(),
package=build_options.package_dir.resolve(),
wheel=repaired_wheel,
)
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir(exist_ok=True)
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
if build_options.test_sources:
# only create test_cwd if it doesn't already exist - it
# may have been created during a previous `testing_arch`
if not test_cwd.exists():
test_cwd.mkdir()
copy_test_sources(
build_options.test_sources,
Path.cwd(),
test_cwd,
)
else:
# Use the test_fail.py file to raise a nice error if the user
# tries to run tests in the cwd
test_cwd.mkdir(exist_ok=True)
(test_cwd / "test_fail.py").write_text(
resources.TEST_FAIL_CWD_FILE.read_text()
)
shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
# we're all done here; move it to output (overwrite existing)
output_wheel = None
if compatible_wheel is None:
with contextlib.suppress(FileNotFoundError):
(build_options.output_dir / repaired_wheel.name).unlink()
shutil.move(str(repaired_wheel), build_options.output_dir)
built_wheels.append(build_options.output_dir / repaired_wheel.name)
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
log.warning(
f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
# clean up
shutil.rmtree(identifier_tmp_dir)
log.build_end()
log.build_end(output_wheel)
except subprocess.CalledProcessError as error:
log.step_end_with_error(
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
)
sys.exit(1)
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
+555
View File
@@ -0,0 +1,555 @@
import dataclasses
import functools
import json
import os
import shutil
import subprocess
import sys
import tomllib
import typing
from collections.abc import Set
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Final, TypedDict
from filelock import FileLock
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture
from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.frontend import get_build_frontend_extra_flags
from cibuildwheel.logger import log
from cibuildwheel.options import Options
from cibuildwheel.selector import BuildSelector
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call, shell
from cibuildwheel.util.file import (
CIBW_CACHE_PATH,
copy_test_sources,
download,
extract_tar,
extract_zip,
move_file,
)
from cibuildwheel.util.helpers import prepare_command, unwrap, unwrap_preserving_paragraphs
from cibuildwheel.util.packaging import find_compatible_wheel, get_pip_version
from cibuildwheel.util.python_build_standalone import (
PythonBuildStandaloneError,
create_python_build_standalone_environment,
)
from cibuildwheel.venv import constraint_flags, virtualenv
IS_WIN: Final[bool] = sys.platform.startswith("win")
@dataclasses.dataclass(frozen=True, kw_only=True)
class PythonConfiguration:
version: str
identifier: str
default_pyodide_version: str
node_version: str
class PyodideXBuildEnvInfoVersionRange(TypedDict):
min: str | None
max: str | None
class PyodideXBuildEnvInfo(TypedDict):
version: str
python: str
emscripten: str
pyodide_build: PyodideXBuildEnvInfoVersionRange
compatible: bool
@functools.cache
def ensure_node(major_version: str) -> Path:
with resources.NODEJS.open("rb") as f:
loaded_file = tomllib.load(f)
version = str(loaded_file[major_version])
base_url = str(loaded_file["url"])
ext = "zip" if IS_WIN else "tar.xz"
platform = "win" if IS_WIN else ("darwin" if sys.platform.startswith("darwin") else "linux")
linux_arch = Architecture.native_arch("linux")
assert linux_arch is not None
arch = {"x86_64": "x64", "i686": "x86", "aarch64": "arm64"}.get(
linux_arch.value, linux_arch.value
)
name = f"node-{version}-{platform}-{arch}"
path = CIBW_CACHE_PATH / name
with FileLock(str(path) + ".lock"):
if not path.exists():
url = f"{base_url}{version}/{name}.{ext}"
with TemporaryDirectory() as tmp_path:
archive = Path(tmp_path) / f"{name}.{ext}"
download(url, archive)
if ext == "zip":
extract_zip(archive, path.parent)
else:
extract_tar(archive, path.parent)
assert path.exists()
if not IS_WIN:
return path / "bin"
return path
def install_emscripten(tmp: Path, version: str) -> Path:
url = f"https://github.com/emscripten-core/emsdk/archive/refs/tags/{version}.zip"
installation_path = CIBW_CACHE_PATH / f"emsdk-{version}"
emsdk_path = installation_path / f"emsdk-{version}/emsdk"
emcc_path = installation_path / f"emsdk-{version}/upstream/emscripten/emcc"
with FileLock(f"{installation_path}.lock"):
if installation_path.exists():
return emcc_path
emsdk_zip = tmp / "emsdk.zip"
download(url, emsdk_zip)
installation_path.mkdir()
extract_zip(emsdk_zip, installation_path)
call(emsdk_path, "install", version)
call(emsdk_path, "activate", version)
return emcc_path
def get_all_xbuildenv_version_info(env: dict[str, str]) -> list[PyodideXBuildEnvInfo]:
xbuildenvs_info_str = call(
"pyodide",
"xbuildenv",
"search",
"--json",
"--all",
env=env,
cwd=CIBW_CACHE_PATH,
capture_stdout=True,
).strip()
xbuildenvs_info = json.loads(xbuildenvs_info_str)
if "environments" not in xbuildenvs_info:
msg = f"Invalid xbuildenvs info, got {xbuildenvs_info}"
raise ValueError(msg)
return typing.cast("list[PyodideXBuildEnvInfo]", xbuildenvs_info["environments"])
def get_xbuildenv_version_info(
env: dict[str, str], version: str, pyodide_build_version: str
) -> PyodideXBuildEnvInfo:
xbuildenvs_info = get_all_xbuildenv_version_info(env)
for xbuildenv_info in xbuildenvs_info:
if xbuildenv_info["version"] == version:
return xbuildenv_info
msg = unwrap(f"""
Could not find Pyodide cross-build environment version {version} in the available
versions as reported by pyodide-build v{pyodide_build_version}.
Available pyodide xbuildenv versions are:
{", ".join(e["version"] for e in xbuildenvs_info if e["compatible"])}
""")
raise errors.FatalError(msg)
# The default pyodide xbuildenv version that's specified in
# build-platforms.toml is compatible with the pyodide-build version that's
# pinned in the bundled constraints file. But if the user changes
# pyodide-version and/or dependency-constraints in the cibuildwheel config, we
# need to check if the xbuildenv version is compatible with the pyodide-build
# version.
def validate_pyodide_build_version(
xbuildenv_info: PyodideXBuildEnvInfo, pyodide_build_version: str
) -> None:
"""
Validate the Pyodide version is compatible with the installed
pyodide-build version.
"""
pyodide_version = xbuildenv_info["version"]
if not xbuildenv_info["compatible"]:
msg = unwrap_preserving_paragraphs(f"""
The Pyodide xbuildenv version {pyodide_version} is not compatible
with the pyodide-build version {pyodide_build_version}. Please use
the 'pyodide xbuildenv search --all' command to find a compatible
version.
Set the pyodide-build version using the `dependency-constraints`
option, or set the Pyodide xbuildenv version using the
`pyodide-version` option.
""")
raise errors.FatalError(msg)
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 uncoupled from the versions of Pyodide. So, we specify
# both the pyodide-build version and the Pyodide version in the temp path.
xbuildenv_cache_path = CIBW_CACHE_PATH / f"pyodide-build-{pyodide_build_version}"
pyodide_root = xbuildenv_cache_path / pyodide_version / "xbuildenv" / "pyodide-root"
with FileLock(CIBW_CACHE_PATH / "xbuildenv.lock"):
if pyodide_root.exists():
return str(pyodide_root)
# We don't want to mutate env but we need to delete any existing
# PYODIDE_ROOT so copy it first.
env = dict(env)
env.pop("PYODIDE_ROOT", None)
# Install the xbuildenv
call(
"pyodide",
"xbuildenv",
"install",
"--path",
str(xbuildenv_cache_path),
pyodide_version,
env=env,
cwd=CIBW_CACHE_PATH,
)
assert pyodide_root.exists()
return str(pyodide_root)
def get_base_python(tmp: Path, python_configuration: PythonConfiguration) -> Path:
try:
return create_python_build_standalone_environment(
python_version=python_configuration.version,
temp_dir=tmp,
cache_dir=CIBW_CACHE_PATH,
)
except PythonBuildStandaloneError as e:
msg = unwrap(f"""
Failed to create a Python build environment:
{e}
""")
raise errors.FatalError(msg) from e
def setup_python(
tmp: Path,
python_configuration: PythonConfiguration,
constraints_path: Path | None,
environment: ParsedEnvironment,
user_pyodide_version: str | None,
) -> dict[str, str]:
log.step("Installing a base python environment...")
base_python = get_base_python(tmp / "base", python_configuration)
log.step("Setting up build environment...")
pyodide_version = user_pyodide_version or python_configuration.default_pyodide_version
venv_path = tmp / "venv"
env = virtualenv(python_configuration.version, base_python, venv_path, None, use_uv=False)
venv_bin_path = venv_path / "bin"
assert venv_bin_path.exists()
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
# upgrade pip to the version matching our constraints
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
call(
"python",
"-m",
"pip",
"install",
"--upgrade",
"pip",
*constraint_flags(constraints_path),
env=env,
cwd=venv_path,
)
env = environment.as_dictionary(prev_environment=env)
# check what Python version we're on
which_python = call("which", "python", env=env, capture_stdout=True).strip()
print(which_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."
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...")
call(
"pip",
"install",
"--upgrade",
"pyodide-build",
*constraint_flags(constraints_path),
env=env,
)
pyodide_build_version = call(
"python",
"-c",
"from importlib.metadata import version; print(version('pyodide-build'))",
env=env,
capture_stdout=True,
).strip()
xbuildenv_info = get_xbuildenv_version_info(env, pyodide_version, pyodide_build_version)
validate_pyodide_build_version(
xbuildenv_info=xbuildenv_info,
pyodide_build_version=pyodide_build_version,
)
emscripten_version = xbuildenv_info["emscripten"]
log.step(f"Installing Emscripten version: {emscripten_version} ...")
emcc_path = install_emscripten(tmp, emscripten_version)
env["PATH"] = os.pathsep.join([str(emcc_path.parent), env["PATH"]])
log.step(f"Installing Pyodide xbuildenv version: {pyodide_version} ...")
env["PYODIDE_ROOT"] = install_xbuildenv(env, pyodide_build_version, pyodide_version)
return env
def all_python_configurations() -> list[PythonConfiguration]:
full_python_configs = resources.read_python_configs("pyodide")
return [PythonConfiguration(**item) for item in full_python_configs]
def get_python_configurations(
build_selector: BuildSelector,
architectures: Set[Architecture], # noqa: ARG001
) -> list[PythonConfiguration]:
return [c for c in all_python_configurations() if build_selector(c.identifier)]
def build(options: Options, tmp_path: Path) -> None:
python_configurations = get_python_configurations(
options.globals.build_selector, options.globals.architectures
)
if not python_configurations:
return
try:
before_all_options_identifier = python_configurations[0].identifier
before_all_options = options.build_options(before_all_options_identifier)
if before_all_options.before_all:
log.step("Running before_all...")
env = before_all_options.environment.as_dictionary(prev_environment=os.environ)
before_all_prepared = prepare_command(
before_all_options.before_all, project=".", package=before_all_options.package_dir
)
shell(before_all_prepared, env=env)
built_wheels: list[Path] = []
for config in python_configurations:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend
if build_frontend.name == "pip":
msg = "The pyodide platform doesn't support pip frontend"
raise errors.FatalError(msg)
log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier
built_wheel_dir = identifier_tmp_dir / "built_wheel"
repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel"
identifier_tmp_dir.mkdir()
built_wheel_dir.mkdir()
repaired_wheel_dir.mkdir()
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version, variant="pyodide", tmp_dir=identifier_tmp_dir
)
env = setup_python(
tmp=identifier_tmp_dir / "build",
python_configuration=config,
constraints_path=constraints_path,
environment=build_options.environment,
user_pyodide_version=build_options.pyodide_version,
)
pip_version = get_pip_version(env)
# The Pyodide command line runner mounts all directories in the host
# filesystem into the Pyodide file system, except for the custom
# file systems /dev, /lib, /proc, and /tmp. Mounting the mount
# points for alternate file systems causes some mysterious failure
# of the process (it just quits without any clear error).
#
# Because of this, by default Pyodide can't see anything under /tmp.
# This environment variable tells it also to mount our temp
# directory.
oldmounts = ""
extra_mounts = [str(identifier_tmp_dir)]
if Path.cwd().is_relative_to("/tmp"):
extra_mounts.append(str(Path.cwd()))
if "_PYODIDE_EXTRA_MOUNTS" in env:
oldmounts = env["_PYODIDE_EXTRA_MOUNTS"] + ":"
env["_PYODIDE_EXTRA_MOUNTS"] = oldmounts + ":".join(extra_mounts)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
log.step_end()
print(
f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
)
built_wheel = compatible_wheel
else:
if build_options.before_build:
log.step("Running before_build...")
before_build_prepared = prepare_command(
build_options.before_build, project=".", package=build_options.package_dir
)
shell(before_build_prepared, env=env)
log.step("Building wheel...")
extra_flags = get_build_frontend_extra_flags(
build_frontend,
build_options.build_verbosity,
build_options.config_settings,
py38=False,
)
call(
"pyodide",
"build",
build_options.package_dir,
f"--outdir={built_wheel_dir}",
*extra_flags,
env=env,
)
built_wheel = next(built_wheel_dir.glob("*.whl"))
if built_wheel.name.endswith("none-any.whl"):
raise errors.NonPlatformWheelError()
if build_options.repair_command:
log.step("Repairing wheel...")
repair_command_prepared = prepare_command(
build_options.repair_command,
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
package=build_options.package_dir,
project=".",
)
shell(repair_command_prepared, env=env)
log.step_end()
else:
shutil.move(str(built_wheel), repaired_wheel_dir)
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
if repaired_wheel.name in {wheel.name for wheel in built_wheels}:
raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
if build_options.test_command and build_options.test_selector(config.identifier):
log.step("Testing wheel...")
venv_dir = identifier_tmp_dir / "venv-test"
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
virtualenv_env = env.copy()
virtualenv_env["PATH"] = os.pathsep.join(
[
str(ensure_node(config.node_version)),
virtualenv_env["PATH"],
]
)
# pyodide venv uses virtualenv under the hood
# use the pip embedded with virtualenv & disable network updates
virtualenv_create_env = virtualenv_env.copy()
virtualenv_create_env["VIRTUALENV_PIP"] = pip_version
virtualenv_create_env["VIRTUALENV_NO_PERIODIC_UPDATE"] = "1"
call("pyodide", "venv", venv_dir, env=virtualenv_create_env)
virtualenv_env["PATH"] = os.pathsep.join(
[
str(venv_dir / "bin"),
virtualenv_env["PATH"],
]
)
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
virtualenv_env = build_options.test_environment.as_dictionary(
prev_environment=virtualenv_env
)
# check that we are using the Python from the virtual environment
call("which", "python", env=virtualenv_env)
if build_options.before_test:
before_test_prepared = prepare_command(
build_options.before_test,
project=".",
package=build_options.package_dir,
wheel=repaired_wheel,
)
shell(before_test_prepared, env=virtualenv_env)
# install the wheel
call(
"pip",
"install",
f"{repaired_wheel}{build_options.test_extras}",
env=virtualenv_env,
)
# test the wheel
if build_options.test_requires:
call("pip", "install", *build_options.test_requires, env=virtualenv_env)
# run the tests from a temp dir, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
test_command_prepared = prepare_command(
build_options.test_command,
project=Path.cwd(),
package=build_options.package_dir.resolve(),
)
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir(exist_ok=True)
if build_options.test_sources:
copy_test_sources(
build_options.test_sources,
Path.cwd(),
test_cwd,
)
else:
# Use the test_fail.py file to raise a nice error if the user
# tries to run tests in the cwd
(test_cwd / "test_fail.py").write_text(resources.TEST_FAIL_CWD_FILE.read_text())
shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
# we're all done here; move it to output (overwrite existing)
output_wheel: Path | None = None
if compatible_wheel is None:
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
log.warning(
f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
log.build_end(output_wheel)
except subprocess.CalledProcessError as error:
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
@@ -1,47 +1,36 @@
from __future__ import annotations
import dataclasses
import json
import os
import platform as platform_module
import shutil
import subprocess
import sys
import textwrap
from collections.abc import MutableMapping, Sequence, Set
from contextlib import suppress
from dataclasses import dataclass
from functools import lru_cache
from functools import cache
from pathlib import Path
from zipfile import ZipFile
from typing import assert_never
from filelock import FileLock
from packaging.version import Version
from ._compat.typing import assert_never
from .architecture import Architecture
from .environment import ParsedEnvironment
from .logger import log
from .options import Options
from .typing import PathOrStr
from .util import (
from cibuildwheel import errors
from cibuildwheel.architecture import Architecture
from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.frontend import BuildFrontendName, get_build_frontend_extra_flags
from cibuildwheel.logger import log
from cibuildwheel.options import Options
from cibuildwheel.selector import BuildSelector
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call, shell
from cibuildwheel.util.file import (
CIBW_CACHE_PATH,
AlreadyBuiltWheelError,
BuildFrontendConfig,
BuildFrontendName,
BuildSelector,
NonPlatformWheelError,
call,
copy_test_sources,
download,
find_compatible_wheel,
get_build_verbosity_extra_flags,
get_pip_version,
prepare_command,
read_python_configs,
shell,
split_config_settings,
test_fail_cwd_file,
unwrap,
virtualenv,
extract_zip,
move_file,
)
from cibuildwheel.util.helpers import prepare_command, unwrap
from cibuildwheel.util.packaging import find_compatible_wheel, get_pip_version
from cibuildwheel.venv import constraint_flags, find_uv, virtualenv
def get_nuget_args(
@@ -68,21 +57,31 @@ def get_nuget_args(
]
@dataclass(frozen=True)
@dataclasses.dataclass(frozen=True, kw_only=True)
class PythonConfiguration:
version: str
arch: str
identifier: str
url: str | None = None
@property
def arch(self) -> str:
return {
"win32": "32",
"win_amd64": "64",
"win_arm64": "ARM64",
}[self.identifier.split("-")[-1]]
def all_python_configurations() -> list[PythonConfiguration]:
config_dicts = resources.read_python_configs("windows")
return [PythonConfiguration(**item) for item in config_dicts]
def get_python_configurations(
build_selector: BuildSelector,
architectures: Set[Architecture],
) -> list[PythonConfiguration]:
full_python_configs = read_python_configs("windows")
python_configurations = [PythonConfiguration(**item) for item in full_python_configs]
python_configurations = all_python_configurations()
map_arch = {"32": Architecture.x86, "64": Architecture.AMD64, "ARM64": Architecture.ARM64}
@@ -96,12 +95,7 @@ def get_python_configurations(
return python_configurations
def extract_zip(zip_src: Path, dest: Path) -> None:
with ZipFile(zip_src) as zip_:
zip_.extractall(dest)
@lru_cache(maxsize=None)
@cache
def _ensure_nuget() -> Path:
nuget = CIBW_CACHE_PATH / "nuget.exe"
with FileLock(str(nuget) + ".lock"):
@@ -143,6 +137,20 @@ def install_pypy(tmp: Path, arch: str, url: str) -> Path:
return installation_path / "python.exe"
def install_graalpy(tmp: Path, url: str) -> Path:
zip_filename = url.rsplit("/", 1)[-1]
extension = ".zip"
assert zip_filename.endswith(extension)
installation_path = CIBW_CACHE_PATH / zip_filename[: -len(extension)]
with FileLock(str(installation_path) + ".lock"):
if not installation_path.exists():
graalpy_zip = tmp / zip_filename
download(url, graalpy_zip)
# Extract to the parent directory because the zip file still contains a directory
extract_zip(graalpy_zip, installation_path.parent)
return installation_path / "bin" / "graalpy.exe"
def setup_setuptools_cross_compile(
tmp: Path,
python_configuration: PythonConfiguration,
@@ -157,6 +165,16 @@ def setup_setuptools_cross_compile(
# set the platform name
map_plat = {"32": "win32", "64": "win-amd64", "ARM64": "win-arm64"}
plat_name = map_plat[python_configuration.arch]
# Set environment variable so that setuptools._distutils.get_platform()
# identifies the target, not the host
vscmd_arg_tgt_arch = {"32": "x86", "64": "x64", "ARM64": "arm64"}
current_tgt_arch = vscmd_arg_tgt_arch[python_configuration.arch]
if (env.get("VSCMD_ARG_TGT_ARCH") or current_tgt_arch) != current_tgt_arch:
msg = f"VSCMD_ARG_TGT_ARCH must be set to {current_tgt_arch!r}, got {env['VSCMD_ARG_TGT_ARCH']!r}. If you're setting up MSVC yourself (e.g. using vcvarsall.bat or msvc-dev-cmd), make sure to target the right architecture. Alternatively, run cibuildwheel without configuring MSVC, and let the build backend handle it."
raise errors.FatalError(msg)
env["VSCMD_ARG_TGT_ARCH"] = current_tgt_arch
# (This file must be default/locale encoding, so we can't pass 'encoding')
distutils_cfg.write_text(
textwrap.dedent(
@@ -220,13 +238,18 @@ def setup_rust_cross_compile(
)
def can_use_uv(python_configuration: PythonConfiguration) -> bool:
conditions = (not python_configuration.identifier.startswith("pp38-"),)
return all(conditions)
def setup_python(
tmp: Path,
python_configuration: PythonConfiguration,
dependency_constraint_flags: Sequence[PathOrStr],
dependency_constraint: Path | None,
environment: ParsedEnvironment,
build_frontend: BuildFrontendName,
) -> dict[str, str]:
) -> tuple[Path, dict[str, str]]:
tmp.mkdir()
implementation_id = python_configuration.identifier.split("-")[0]
python_libs_base = None
@@ -244,100 +267,125 @@ def setup_python(
elif implementation_id.startswith("pp"):
assert python_configuration.url is not None
base_python = install_pypy(tmp, python_configuration.arch, python_configuration.url)
elif implementation_id.startswith("gp"):
base_python = install_graalpy(tmp, python_configuration.url or "")
else:
msg = "Unknown Python implementation"
raise ValueError(msg)
assert base_python.exists()
if build_frontend == "build[uv]" and not can_use_uv(python_configuration):
build_frontend = "build"
use_uv = build_frontend in {"build[uv]", "uv"}
uv_path = find_uv()
log.step("Setting up build environment...")
venv_path = tmp / "venv"
env = virtualenv(
python_configuration.version, base_python, venv_path, dependency_constraint_flags
python_configuration.version,
base_python,
venv_path,
dependency_constraint,
use_uv=use_uv,
)
# set up environment variables for run_with_env
env["PYTHON_VERSION"] = python_configuration.version
env["PYTHON_ARCH"] = python_configuration.arch
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
# pip older than 21.3 builds executables such as pip.exe for x64 platform.
# The first re-install of pip updates pip module but builds pip.exe using
# the old pip which still generates x64 executable. But the second
# re-install uses updated pip and correctly builds pip.exe for the target.
# This can be removed once ARM64 Pythons (currently 3.9 and 3.10) bundle
# pip versions newer than 21.3.
if python_configuration.arch == "ARM64" and Version(get_pip_version(env)) < Version("21.3"):
call(
"python",
"-m",
"pip",
"install",
"--force-reinstall",
"--upgrade",
"pip",
*dependency_constraint_flags,
env=env,
cwd=venv_path,
)
# upgrade pip to the version matching our constraints
# if necessary, reinstall it to ensure that it's available on PATH as 'pip.exe'
call(
"python",
"-m",
"pip",
"install",
"--upgrade",
"pip",
*dependency_constraint_flags,
env=env,
cwd=venv_path,
)
env.pop("UV_PYTHON", None)
if not use_uv:
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
# update env with results from CIBW_ENVIRONMENT
env = environment.as_dictionary(prev_environment=env)
# check what Python version we're on
call("where", "python", env=env)
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"):
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)
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()
if where_python != str(venv_path / "Scripts" / "python.exe"):
print(
"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.",
file=sys.stderr,
)
sys.exit(1)
# check what pip version we're on
assert (venv_path / "Scripts" / "pip.exe").exists()
where_pip = call("where", "pip", env=env, capture_stdout=True).splitlines()[0].strip()
if where_pip.strip() != str(venv_path / "Scripts" / "pip.exe"):
print(
"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.",
file=sys.stderr,
)
sys.exit(1)
call("pip", "--version", env=env)
if not use_uv:
assert (venv_path / "Scripts" / "pip.exe").exists()
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"):
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)
call("pip", "--version", env=env)
log.step("Installing build tools...")
if build_frontend == "build":
call(
"pip",
"install",
"--upgrade",
"build[virtualenv]",
*dependency_constraint_flags,
env=env,
)
match build_frontend:
case "build":
call(
"pip",
"install",
"--upgrade",
"build[virtualenv]",
*constraint_flags(dependency_constraint),
env=env,
)
case "build[uv]":
assert uv_path is not None
call(
uv_path,
"pip",
"install",
"--python",
where_python,
"--upgrade",
"build[virtualenv]",
*constraint_flags(dependency_constraint),
env=env,
)
if python_libs_base:
# Set up the environment for various backends to enable cross-compilation
setup_setuptools_cross_compile(tmp, python_configuration, python_libs_base, env)
setup_rust_cross_compile(tmp, python_configuration, python_libs_base, env)
return env
if implementation_id.startswith("gp311"):
# GraalPy 24 fails to discover compilers, setup the relevant environment
# variables. Adapted from
# https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt
# Remove when GraalPy 24.x is dropped.
vcpath = call(
Path(os.environ["PROGRAMFILES(X86)"])
/ "Microsoft Visual Studio"
/ "Installer"
/ "vswhere.exe",
"-products",
"*",
"-latest",
"-property",
"installationPath",
capture_stdout=True,
).strip()
log.notice(f"Discovering Visual Studio for GraalPy at {vcpath}")
vcvars_file = tmp / "vcvars.json"
call(
f"{vcpath}\\Common7\\Tools\\vsdevcmd.bat",
"-no_logo",
"-arch=amd64",
"-host_arch=amd64",
"&&",
"python",
"-c",
# this command needs to be one line for Windows reasons
"import sys, json, pathlib, os; pathlib.Path(sys.argv[1]).write_text(json.dumps(dict(os.environ)))",
vcvars_file,
env=env,
)
with open(vcvars_file, encoding="utf-8") as f:
vcvars = json.load(f)
env.update(vcvars)
return base_python, env
def build(options: Options, tmp_path: Path) -> None:
@@ -348,6 +396,8 @@ def build(options: Options, tmp_path: Path) -> None:
if not python_configurations:
return
uv_path = find_uv()
try:
before_all_options_identifier = python_configurations[0].identifier
before_all_options = options.build_options(before_all_options_identifier)
@@ -364,7 +414,8 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations:
build_options = options.build_options(config.identifier)
build_frontend = build_options.build_frontend or BuildFrontendConfig("pip")
build_frontend = build_options.build_frontend
use_uv = build_frontend.name in {"build[uv]", "uv"} and can_use_uv(config)
log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier
@@ -372,21 +423,20 @@ def build(options: Options, tmp_path: Path) -> None:
built_wheel_dir = identifier_tmp_dir / "built_wheel"
repaired_wheel_dir = identifier_tmp_dir / "repaired_wheel"
dependency_constraint_flags: Sequence[PathOrStr] = []
if build_options.dependency_constraints:
dependency_constraint_flags = [
"-c",
build_options.dependency_constraints.get_for_python_version(config.version),
]
constraints_path = build_options.dependency_constraints.get_for_python_version(
version=config.version,
tmp_dir=identifier_tmp_dir,
)
# install Python
env = setup_python(
base_python, env = setup_python(
identifier_tmp_dir / "build",
config,
dependency_constraint_flags,
constraints_path,
build_options.environment,
build_frontend.name,
)
pip_version = None if use_uv else get_pip_version(env)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
if compatible_wheel:
@@ -409,64 +459,76 @@ def build(options: Options, tmp_path: Path) -> None:
log.step("Building wheel...")
built_wheel_dir.mkdir()
extra_flags = split_config_settings(
build_options.config_settings, build_frontend.name
extra_flags = get_build_frontend_extra_flags(
build_frontend,
build_options.build_verbosity,
build_options.config_settings,
py38=config.identifier[1:].startswith("p38"),
)
extra_flags += build_frontend.args
build_env = env.copy()
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
if build_options.dependency_constraints:
constraints_path = build_options.dependency_constraints.get_for_python_version(
config.version
if (
config.identifier.startswith("gp311")
and build_frontend.name == "build"
and "--no-isolation" not in extra_flags
and "-n" not in extra_flags
):
# GraalPy 24 fails to discover its standard library when a venv is created
# from a virtualenv seeded executable. See
# https://github.com/oracle/graalpython/issues/491 and remove this once
# GraalPy 24 is dropped.
log.notice(
"Disabling build isolation to workaround GraalPy bug. If the build fails, consider using pip or build[uv] as build frontend."
)
# Bug in pip <= 21.1.3 - we can't have a space in the
# constraints file, and pip doesn't support drive letters
# in uhi. After probably pip 21.2, we can use uri. For
# now, use a temporary file.
if " " in str(constraints_path):
assert " " not in str(identifier_tmp_dir)
tmp_file = identifier_tmp_dir / "constraints.txt"
tmp_file.write_bytes(constraints_path.read_bytes())
constraints_path = tmp_file
shell("graalpy -m pip install setuptools wheel", env=env)
extra_flags = [*extra_flags, "-n"]
our_constraints = str(constraints_path)
user_constraints = build_env.get("PIP_CONSTRAINT")
build_env["PIP_CONSTRAINT"] = " ".join(
c for c in [our_constraints, user_constraints] if c
)
match build_frontend.name:
case "pip":
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369
call(
"python",
"-m",
"pip",
"wheel",
options.globals.package_dir.resolve(),
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
env=env,
)
case "build" | "build[uv]":
if (
use_uv
and "--no-isolation" not in extra_flags
and "-n" not in extra_flags
):
extra_flags.append("--installer=uv")
if build_frontend.name == "pip":
extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369
call(
"python",
"-m",
"pip",
"wheel",
options.globals.package_dir.resolve(),
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
env=build_env,
)
elif build_frontend.name == "build":
if not 0 <= build_options.build_verbosity < 2:
msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
log.warning(msg)
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
else:
assert_never(build_frontend)
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=env,
)
case "uv":
assert uv_path is not None
call(
uv_path,
"build",
f"--python={base_python}",
build_options.package_dir,
"--wheel",
f"--out-dir={built_wheel_dir}",
*extra_flags,
env=env,
)
case _:
assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl"))
@@ -474,7 +536,7 @@ def build(options: Options, tmp_path: Path) -> None:
repaired_wheel_dir.mkdir()
if built_wheel.name.endswith("none-any.whl"):
raise NonPlatformWheelError()
raise errors.NonPlatformWheelError()
if build_options.repair_command:
log.step("Repairing wheel...")
@@ -482,15 +544,20 @@ def build(options: Options, tmp_path: Path) -> None:
build_options.repair_command,
wheel=built_wheel,
dest_dir=repaired_wheel_dir,
package=build_options.package_dir,
project=".",
)
shell(repair_command_prepared, env=env)
else:
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}:
raise AlreadyBuiltWheelError(repaired_wheel.name)
raise errors.AlreadyBuiltWheelError(repaired_wheel.name)
test_selected = options.globals.test_selector(config.identifier)
if test_selected and config.arch == "ARM64" != platform_module.machine():
@@ -508,28 +575,24 @@ def build(options: Options, tmp_path: Path) -> None:
log.step("Testing wheel...")
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
call("pip", "install", "virtualenv", *dependency_constraint_flags, env=env)
venv_dir = identifier_tmp_dir / "venv-test"
virtualenv_env = virtualenv(
config.version,
base_python,
venv_dir,
None,
use_uv=use_uv,
env=env,
pip_version=pip_version,
)
# Use --no-download to ensure determinism by using seed libraries
# built into virtualenv
call("python", "-m", "virtualenv", "--no-download", venv_dir, env=env)
virtualenv_env = env.copy()
virtualenv_env["PATH"] = os.pathsep.join(
[
str(venv_dir / "Scripts"),
virtualenv_env["PATH"],
]
virtualenv_env = build_options.test_environment.as_dictionary(
prev_environment=virtualenv_env
)
# check that we are using the Python from the virtual environment
call("where", "python", env=virtualenv_env)
# TODO remove me once virtualenv provides pip>=24.1b1
if config.version.startswith("3.13."):
call("python", "-m", "pip", "install", "--pre", "-U", "pip", env=virtualenv_env)
if build_options.before_test:
before_test_prepared = prepare_command(
build_options.before_test,
@@ -538,9 +601,16 @@ def build(options: Options, tmp_path: Path) -> None:
)
shell(before_test_prepared, env=virtualenv_env)
pip: Sequence[Path | str]
if use_uv:
assert uv_path is not None
pip = [uv_path, "pip"]
else:
pip = ["pip"]
# install the wheel
call(
"pip",
*pip,
"install",
str(repaired_wheel) + build_options.test_extras,
env=virtualenv_env,
@@ -548,39 +618,50 @@ def build(options: Options, tmp_path: Path) -> None:
# test the wheel
if build_options.test_requires:
call("pip", "install", *build_options.test_requires, env=virtualenv_env)
call(*pip, "install", *build_options.test_requires, env=virtualenv_env)
# run the tests from a temp dir, with an absolute path in the command
# (this ensures that Python runs the tests against the installed wheel
# and not the repo code)
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir()
if build_options.test_sources:
copy_test_sources(
build_options.test_sources,
Path.cwd(),
test_cwd,
)
else:
# Use the test_fail.py file to raise a nice error if the user
# tries to run tests in the cwd
(test_cwd / "test_fail.py").write_text(resources.TEST_FAIL_CWD_FILE.read_text())
test_command_prepared = prepare_command(
build_options.test_command,
project=Path(".").resolve(),
project=Path.cwd(),
package=options.globals.package_dir.resolve(),
wheel=repaired_wheel,
)
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir()
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
# we're all done here; move it to output (remove if already exists)
output_wheel = None
if compatible_wheel is None:
with suppress(FileNotFoundError):
(build_options.output_dir / repaired_wheel.name).unlink()
shutil.move(str(repaired_wheel), build_options.output_dir)
built_wheels.append(build_options.output_dir / repaired_wheel.name)
output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
log.warning(
f"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
# clean up
# (we ignore errors because occasionally Windows fails to unlink a file and we
# don't want to abort a build because of that)
shutil.rmtree(identifier_tmp_dir, ignore_errors=True)
log.build_end()
log.build_end(output_wheel)
except subprocess.CalledProcessError as error:
log.step_end_with_error(
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
)
sys.exit(1)
msg = f"Command {error.cmd} failed with code {error.returncode}. {error.stdout or ''}"
raise errors.FatalError(msg) from error
+42 -43
View File
@@ -1,11 +1,10 @@
from __future__ import annotations
import ast
import configparser
import contextlib
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:
@@ -15,34 +14,16 @@ def get_parent(node: ast.AST | None, depth: int = 1) -> ast.AST | None:
def is_main(parent: ast.AST | None) -> bool:
if parent is None:
return False
# This would be much nicer with 3.10's pattern matching!
if not isinstance(parent, ast.If):
return False
if not isinstance(parent.test, ast.Compare):
return False
try:
(op,) = parent.test.ops
(comp,) = parent.test.comparators
except ValueError:
return False
if not isinstance(op, ast.Eq):
return False
values = {comp, parent.test.left}
mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
if len(mains) != 1:
return False
consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
if len(consts) != 1:
return False
return True
match parent:
case ast.If(test=ast.Compare(left=left, ops=[ast.Eq()], comparators=[comp])):
values = {left, comp}
mains = {x for x in values if isinstance(x, ast.Constant) and x.value == "__main__"}
if len(mains) != 1:
return False
consts = {x for x in values if isinstance(x, ast.Name) and x.id == "__name__"}
return len(consts) == 1
case _:
return False
class Analyzer(ast.NodeVisitor):
@@ -68,12 +49,10 @@ class Analyzer(ast.NodeVisitor):
parent is not None and get_parent(parent) is None and is_main(get_parent(node, 3))
)
if (
node.arg == "python_requires"
and isinstance(node.value, ast.Constant)
and (unnested or name_main_unnested)
):
self.requires_python = node.value.value
match node:
case ast.keyword(arg="python_requires", value=ast.Constant(value=str() as version)):
if unnested or name_main_unnested:
self.requires_python = version
def setup_py_python_requires(content: str) -> str | None:
@@ -86,15 +65,12 @@ def setup_py_python_requires(content: str) -> str | 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"""
# Read in from pyproject.toml:project.requires-python
with contextlib.suppress(FileNotFoundError):
with (package_dir / "pyproject.toml").open("rb") as f1:
info = tomllib.load(f1)
with contextlib.suppress(KeyError, IndexError, TypeError):
return str(info["project"]["requires-python"])
with contextlib.suppress(KeyError, IndexError, TypeError):
return str((pyproject_toml or {})["project"]["requires-python"])
# Read in from setup.cfg:options.python_requires
config = configparser.ConfigParser()
@@ -108,3 +84,26 @@ def get_requires_python_str(package_dir: Path) -> str | None:
return setup_py_python_requires(f2.read())
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)
+1
View File
@@ -0,0 +1 @@
import _cross_venv; _cross_venv.initialize()
+87
View File
@@ -0,0 +1,87 @@
# This module is copied into the site-packages directory of an Android build environment, and
# activated via a .pth file when we want the environment to simulate Android.
import os
import platform
import re
import sys
import sysconfig
from pathlib import Path
from typing import Any
def initialize() -> None:
if not (host_triplet := os.environ.get("CIBW_HOST_TRIPLET")):
return
# os ######################################################################
def cross_os_uname() -> os.uname_result:
return os.uname_result(
(
"Linux",
"localhost",
# The Linux kernel version and release are unlikely to be significant, but return
# realistic values anyway (from an API level 24 emulator).
"3.18.91+",
"#1 SMP PREEMPT Tue Jan 9 20:35:43 UTC 2018",
host_triplet.split("-")[0],
)
)
os.name = "posix"
os.uname = cross_os_uname
# platform ################################################################
#
# We can't determine the user-visible Android version number from the API level, so return a
# string which will work fine for display, but will fail to parse as a version number.
def cross_android_ver(*args: Any, **kwargs: Any) -> platform.AndroidVer:
return platform.AndroidVer(
release=f"API level {cross_getandroidapilevel()}",
api_level=cross_getandroidapilevel(),
manufacturer="Google",
model="sdk_gphone64",
device="emu64",
is_emulator=True,
)
# platform.uname, platform.system etc. are all implemented in terms of platform.android_ver.
platform.android_ver = cross_android_ver
# sys #####################################################################
def cross_getandroidapilevel() -> int:
api_level = sysconfig.get_config_var("ANDROID_API_LEVEL")
assert isinstance(api_level, int)
return api_level
# Some packages may recognize sys.cross_compiling from the crossenv tool.
sys.cross_compiling = True # type: ignore[attr-defined]
sys.getandroidapilevel = cross_getandroidapilevel # type: ignore[attr-defined]
sys.implementation._multiarch = host_triplet # type: ignore[attr-defined]
sys.platform = "android"
# Determine the abiflags from the sysconfigdata filename.
sysconfigdata_path = next(Path(__file__).parent.glob("_sysconfigdata_*.py"))
abiflags_match = re.match(r"_sysconfigdata_(.*?)_", sysconfigdata_path.name)
assert abiflags_match is not None
sys.abiflags = abiflags_match[1]
# sysconfig ###############################################################
#
# Load the sysconfigdata file, generating its name from sys.abiflags,
# sys.platform, and sys.implementation._multiarch.
sysconfig._init_config_vars() # type: ignore[attr-defined]
# We don't change the actual sys.base_prefix and base_exec_prefix, because that
# could have unpredictable effects. Instead, we change the sysconfig variables
# used by sysconfig.get_paths().
vars = sysconfig.get_config_vars()
try:
host_prefix = vars["host_prefix"] # This variable was added in Python 3.14.
except KeyError:
host_prefix = vars["exec_prefix"]
vars["installed_base"] = vars["installed_platbase"] = host_prefix
# sysconfig.get_platform, which determines the wheel tag, is implemented in terms of
# sys.platform, sysconfig.get_config_var("ANDROID_API_LEVEL") (see localized_vars in
# android.py), and os.uname.
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
import sys
from pathlib import Path
def main() -> None:
# CIBW_HOST_TRIPLET is set in the android_env to the Android target triplet.
target = os.environ.get("CIBW_HOST_TRIPLET")
cmd_name = Path(sys.argv[0]).name
# Find the real command in PATH, excluding the current script's directory
path_env = os.environ.get("PATH", "")
script_dir = Path(__file__).resolve().parent
paths = path_env.split(os.pathsep)
# Filter out the script directory to avoid recursion
filtered_paths = [p for p in paths if Path(p).resolve() != script_dir]
filtered_path_env = os.pathsep.join(filtered_paths)
real_cmd = shutil.which(cmd_name, path=filtered_path_env)
if not real_cmd:
sys.stderr.write(f"cibuildwheel: Error: Could not find system {cmd_name}\n")
sys.exit(1)
# If we have a target (i.e. we are in the android_env), try to install it.
if target:
# Check if rustup is available to install the target
rustup_path = shutil.which("rustup", path=filtered_path_env)
if rustup_path:
try:
# We call rustup to ensure the target is installed.
subprocess.run(
[rustup_path, "target", "add", target],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
)
except subprocess.CalledProcessError as e:
sys.stderr.write(
f"cibuildwheel: Error: Failed to install Rust target {target}: {e.stderr}\n"
)
sys.exit(1)
# Execute the real command
os.execv(real_cmd, [real_cmd, *sys.argv[1:]])
if __name__ == "__main__":
main()
+142 -62
View File
@@ -1,7 +1,5 @@
[linux]
python_configurations = [
{ identifier = "cp36-manylinux_x86_64", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_x86_64", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -9,8 +7,8 @@ python_configurations = [
{ identifier = "cp312-manylinux_x86_64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-manylinux_i686", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_i686", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-manylinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-manylinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -18,12 +16,14 @@ python_configurations = [
{ identifier = "cp312-manylinux_i686", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "pp37-manylinux_x86_64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "cp314-manylinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ 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 = "pp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
{ 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 = "pp311-manylinux_x86_64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
{ identifier = "gp311_242-manylinux_x86_64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
{ identifier = "gp312_250-manylinux_x86_64", version = "3.12", path_str = "/opt/python/graalpy312-graalpy250_312_native" },
{ identifier = "cp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -31,8 +31,8 @@ python_configurations = [
{ identifier = "cp312-manylinux_aarch64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-manylinux_ppc64le", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_ppc64le", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-manylinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-manylinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -40,8 +40,8 @@ python_configurations = [
{ identifier = "cp312-manylinux_ppc64le", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-manylinux_s390x", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_s390x", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-manylinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-manylinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -49,16 +49,36 @@ python_configurations = [
{ 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 = "cp313t-manylinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "pp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "cp314-manylinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ 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 = "cp314-manylinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-manylinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_riscv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp312-manylinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-manylinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-manylinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp314-manylinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-manylinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ 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 = "pp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
{ identifier = "pp37-manylinux_i686", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp311-manylinux_aarch64", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
{ identifier = "gp311_242-manylinux_aarch64", version = "3.11", path_str = "/opt/python/graalpy311-graalpy242_311_native" },
{ identifier = "gp312_250-manylinux_aarch64", version = "3.12", path_str = "/opt/python/graalpy312-graalpy250_312_native" },
{ 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 = "pp310-manylinux_i686", version = "3.10", path_str = "/opt/python/pp310-pypy310_pp73" },
{ 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 = "pp311-manylinux_i686", version = "3.11", path_str = "/opt/python/pp311-pypy311_pp73" },
{ identifier = "cp38-musllinux_x86_64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_x86_64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_x86_64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -66,8 +86,8 @@ python_configurations = [
{ identifier = "cp312-musllinux_x86_64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_x86_64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-musllinux_i686", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_i686", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-musllinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_x86_64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-musllinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -75,8 +95,8 @@ python_configurations = [
{ identifier = "cp312-musllinux_i686", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_i686", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-musllinux_aarch64", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_aarch64", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-musllinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_i686", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-musllinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -84,8 +104,8 @@ python_configurations = [
{ identifier = "cp312-musllinux_aarch64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_aarch64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-musllinux_ppc64le", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_ppc64le", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-musllinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_aarch64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-musllinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -93,8 +113,8 @@ python_configurations = [
{ identifier = "cp312-musllinux_ppc64le", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_ppc64le", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp36-musllinux_s390x", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_s390x", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp314-musllinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_ppc64le", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-musllinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
@@ -102,12 +122,30 @@ python_configurations = [
{ 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 = "cp313t-musllinux_s390x", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp314-musllinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_s390x", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ 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" },
{ identifier = "cp314-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_armv7l", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
{ identifier = "cp38-musllinux_riscv64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_riscv64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_riscv64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_riscv64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
{ identifier = "cp312-musllinux_riscv64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp313-musllinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313" },
{ identifier = "cp313t-musllinux_riscv64", version = "3.13", path_str = "/opt/python/cp313-cp313t" },
{ identifier = "cp314-musllinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314" },
{ identifier = "cp314t-musllinux_riscv64", version = "3.14", path_str = "/opt/python/cp314-cp314t" },
]
[macos]
python_configurations = [
{ identifier = "cp36-macosx_x86_64", version = "3.6", url = "https://www.python.org/ftp/python/3.6.8/python-3.6.8-macosx10.9.pkg" },
{ identifier = "cp37-macosx_x86_64", version = "3.7", url = "https://www.python.org/ftp/python/3.7.9/python-3.7.9-macosx10.9.pkg" },
{ identifier = "cp38-macosx_x86_64", version = "3.8", url = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg" },
{ identifier = "cp38-macosx_arm64", version = "3.8", url = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg" },
{ identifier = "cp38-macosx_universal2", version = "3.8", url = "https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg" },
@@ -120,49 +158,91 @@ 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_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 = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.3/python-3.12.3-macos11.pkg" },
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-macos11.pkg" },
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.0/python-3.13.0b1-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 = "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.10/python-3.12.10-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.12/python-3.13.12-macos11.pkg" },
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.12/python-3.13.12-macos11.pkg" },
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.12/python-3.13.12-macos11.pkg" },
{ identifier = "cp313t-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.12/python-3.13.12-macos11.pkg" },
{ identifier = "cp313t-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.12/python-3.13.12-macos11.pkg" },
{ identifier = "cp313t-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.12/python-3.13.12-macos11.pkg" },
{ identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg" },
{ identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg" },
{ identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg" },
{ identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg" },
{ identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg" },
{ identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-macos11.pkg" },
{ 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 = "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 = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.16-macos_x86_64.tar.bz2" },
{ identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-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.19-macos_x86_64.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.21-macos_x86_64.tar.bz2" },
{ identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.21-macos_arm64.tar.bz2" },
{ identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-amd64.tar.gz" },
{ identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-aarch64.tar.gz" },
{ identifier = "gp312_250-macosx_x86_64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.1/graalpy-25.0.1-macos-amd64.tar.gz" },
{ identifier = "gp312_250-macosx_arm64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.2/graalpy-25.0.2-macos-aarch64.tar.gz" },
]
[windows]
python_configurations = [
{ identifier = "cp36-win32", version = "3.6.8", arch = "32" },
{ identifier = "cp36-win_amd64", version = "3.6.8", arch = "64" },
{ identifier = "cp37-win32", version = "3.7.9", arch = "32" },
{ identifier = "cp37-win_amd64", version = "3.7.9", arch = "64" },
{ identifier = "cp38-win32", version = "3.8.10", arch = "32" },
{ identifier = "cp38-win_amd64", version = "3.8.10", arch = "64" },
{ identifier = "cp39-win32", version = "3.9.13", arch = "32" },
{ identifier = "cp39-win_amd64", version = "3.9.13", arch = "64" },
{ identifier = "cp310-win32", version = "3.10.11", arch = "32" },
{ identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
{ identifier = "cp311-win32", version = "3.11.9", arch = "32" },
{ identifier = "cp311-win_amd64", version = "3.11.9", arch = "64" },
{ identifier = "cp312-win32", version = "3.12.3", arch = "32" },
{ identifier = "cp312-win_amd64", version = "3.12.3", arch = "64" },
{ identifier = "cp313-win32", version = "3.13.0-b1", arch = "32" },
{ identifier = "cp313t-win32", version = "3.13.0-b1", arch = "32" },
{ identifier = "cp313-win_amd64", version = "3.13.0-b1", arch = "64" },
{ identifier = "cp313t-win_amd64", version = "3.13.0-b1", arch = "64" },
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
{ identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
{ identifier = "cp311-win_arm64", version = "3.11.9", arch = "ARM64" },
{ identifier = "cp312-win_arm64", version = "3.12.3", arch = "ARM64" },
{ identifier = "cp313-win_arm64", version = "3.13.0-b1", arch = "ARM64" },
{ identifier = "cp313t-win_arm64", version = "3.13.0-b1", 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 = "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 = "pp310-win_amd64", version = "3.10", arch = "64", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.16-win64.zip" },
{ identifier = "cp38-win32", version = "3.8.10" },
{ identifier = "cp38-win_amd64", version = "3.8.10" },
{ identifier = "cp39-win32", version = "3.9.13" },
{ identifier = "cp39-win_amd64", version = "3.9.13" },
{ identifier = "cp310-win32", version = "3.10.11" },
{ identifier = "cp310-win_amd64", version = "3.10.11" },
{ identifier = "cp311-win32", version = "3.11.9" },
{ identifier = "cp311-win_amd64", version = "3.11.9" },
{ identifier = "cp312-win32", version = "3.12.10" },
{ identifier = "cp312-win_amd64", version = "3.12.10" },
{ identifier = "cp313-win32", version = "3.13.12" },
{ identifier = "cp313t-win32", version = "3.13.12" },
{ identifier = "cp313-win_amd64", version = "3.13.12" },
{ identifier = "cp313t-win_amd64", version = "3.13.12" },
{ identifier = "cp314-win32", version = "3.14.3" },
{ identifier = "cp314t-win32", version = "3.14.3" },
{ identifier = "cp314-win_amd64", version = "3.14.3" },
{ identifier = "cp314t-win_amd64", version = "3.14.3" },
{ identifier = "cp39-win_arm64", version = "3.9.10" },
{ identifier = "cp310-win_arm64", version = "3.10.11" },
{ identifier = "cp311-win_arm64", version = "3.11.9" },
{ identifier = "cp312-win_arm64", version = "3.12.10" },
{ identifier = "cp313-win_arm64", version = "3.13.12" },
{ identifier = "cp313t-win_arm64", version = "3.13.12" },
{ identifier = "cp314-win_arm64", version = "3.14.3" },
{ identifier = "cp314t-win_arm64", version = "3.14.3" },
{ identifier = "pp38-win_amd64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
{ identifier = "pp39-win_amd64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-win64.zip" },
{ identifier = "pp310-win_amd64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-win64.zip" },
{ identifier = "pp311-win_amd64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.21-win64.zip" },
{ identifier = "gp311_242-win_amd64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-windows-amd64.zip" },
{ identifier = "gp312_250-win_amd64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.2/graalpy-25.0.2-windows-amd64.zip" },
]
[pyodide]
python_configurations = [
{ identifier = "cp312-pyodide_wasm32", version = "3.12", default_pyodide_version = "0.27.7", node_version = "v22" },
{ identifier = "cp313-pyodide_wasm32", version = "3.13", default_pyodide_version = "0.29.3", node_version = "v22" },
]
[android]
python_configurations = [
{ identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.12/python-3.13.12-aarch64-linux-android.tar.gz" },
{ identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.12/python-3.13.12-x86_64-linux-android.tar.gz" },
{ identifier = "cp314-android_arm64_v8a", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-aarch64-linux-android.tar.gz" },
{ identifier = "cp314-android_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.3/python-3.14.3-x86_64-linux-android.tar.gz" },
]
[ios]
python_configurations = [
{ identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz" },
{ identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz" },
{ identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz" },
{ identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz" },
{ identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz" },
{ identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz" },
]
+499 -11
View File
@@ -1,5 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/pypa/cibuildwheel/blob/main/cibuildwheel/resources/cibuildwheel.schema.json",
"$defs": {
"inherit": {
@@ -10,7 +10,18 @@
],
"default": "none",
"description": "How to inherit the parent's value."
}
},
"enable": {
"enum": [
"cpython-freethreading",
"cpython-prerelease",
"graalpy",
"pyodide-prerelease",
"pypy",
"pypy-eol"
]
},
"description": "A Python version or flavor to enable."
},
"additionalProperties": false,
"description": "cibuildwheel's settings.",
@@ -96,12 +107,14 @@
},
"build-frontend": {
"default": "default",
"description": "Set the tool to use to build, either \"pip\" (default for now) or \"build\"",
"description": "Set the tool to use to build, either \"build\" (default), \"build[uv]\", \"uv\", or \"pip\"",
"oneOf": [
{
"enum": [
"pip",
"build",
"build[uv]",
"uv",
"default"
]
},
@@ -113,6 +126,14 @@
"type": "string",
"pattern": "^build; ?args:"
},
{
"type": "string",
"pattern": "^build\\[uv\\]; ?args:"
},
{
"type": "string",
"pattern": "^uv; ?args:"
},
{
"type": "object",
"additionalProperties": false,
@@ -123,7 +144,9 @@
"name": {
"enum": [
"pip",
"build"
"build",
"build[uv]",
"uv"
]
},
"args": {
@@ -219,9 +242,62 @@
"dependency-versions": {
"default": "pinned",
"description": "Specify how cibuildwheel controls the versions of the tools it uses",
"type": "string",
"oneOf": [
{
"enum": [
"pinned",
"latest"
]
},
{
"type": "string",
"description": "Path to a file containing dependency versions, or inline package specifications, starting with \"packages:\"",
"not": {
"enum": [
"pinned",
"latest"
]
}
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"file": {
"type": "string"
}
}
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"packages": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
],
"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": {
"description": "Set environment variables needed during the build.",
"oneOf": [
@@ -255,17 +331,16 @@
],
"title": "CIBW_ENVIRONMENT_PASS"
},
"free-threaded-support": {
"type": "boolean",
"default": false,
"description": "The project supports free-threaded builds of Python (PEP703)",
"title": "CIBW_FREE_THREADED_SUPPORT"
},
"manylinux-aarch64-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
"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": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
@@ -291,6 +366,11 @@
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MANYLINUX_PYPY_X86_64_IMAGE"
},
"manylinux-riscv64-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MANYLINUX_RISCV64_IMAGE"
},
"manylinux-s390x-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
@@ -306,6 +386,11 @@
"description": "Specify alternative manylinux / musllinux container images",
"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": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
@@ -316,6 +401,11 @@
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MUSLLINUX_PPC64LE_IMAGE"
},
"musllinux-riscv64-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MUSLLINUX_RISCV64_IMAGE"
},
"musllinux-s390x-image": {
"type": "string",
"description": "Specify alternative manylinux / musllinux container images",
@@ -326,6 +416,26 @@
"description": "Specify alternative manylinux / musllinux container images",
"title": "CIBW_MUSLLINUX_X86_64_IMAGE"
},
"xbuild-tools": {
"description": "Binaries on the path that should be included in an isolated cross-build environment",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"title": "CIBW_XBUILD_TOOLS"
},
"pyodide-version": {
"type": "string",
"description": "Specify the version of Pyodide to use",
"title": "CIBW_PYODIDE_VERSION"
},
"repair-wheel-command": {
"description": "Execute a shell command to repair each built wheel.",
"oneOf": [
@@ -386,6 +496,36 @@
],
"title": "CIBW_TEST_EXTRAS"
},
"test-sources": {
"description": "Test files that are required by the test environment",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"title": "CIBW_TEST_SOURCES"
},
"test-groups": {
"description": "Install extra groups when testing",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"title": "CIBW_TEST_GROUPS"
},
"test-requires": {
"description": "Install Python dependencies before running the tests",
"oneOf": [
@@ -416,6 +556,57 @@
],
"title": "CIBW_TEST_SKIP"
},
"test-environment": {
"description": "Set environment variables for the test environment",
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"additionalProperties": false,
"patternProperties": {
".+": {
"type": "string"
}
}
}
],
"title": "CIBW_TEST_ENVIRONMENT"
},
"test-runtime": {
"description": "Additional configuration for the test runner",
"oneOf": [
{
"type": "string",
"pattern": "^$"
},
{
"type": "object",
"additionalProperties": false
},
{
"type": "string",
"pattern": "args:"
},
{
"type": "object",
"additionalProperties": false,
"required": [
"args"
],
"properties": {
"args": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
],
"title": "CIBW_TEST_RUNTIME"
},
"overrides": {
"type": "array",
"description": "An overrides array",
@@ -450,6 +641,9 @@
"before-build": {
"$ref": "#/$defs/inherit"
},
"xbuild-tools": {
"$ref": "#/$defs/inherit"
},
"before-test": {
"$ref": "#/$defs/inherit"
},
@@ -474,8 +668,17 @@
"test-extras": {
"$ref": "#/$defs/inherit"
},
"test-sources": {
"$ref": "#/$defs/inherit"
},
"test-requires": {
"$ref": "#/$defs/inherit"
},
"test-environment": {
"$ref": "#/$defs/inherit"
},
"test-runtime": {
"$ref": "#/$defs/inherit"
}
}
},
@@ -512,6 +715,9 @@
"manylinux-aarch64-image": {
"$ref": "#/properties/manylinux-aarch64-image"
},
"manylinux-armv7l-image": {
"$ref": "#/properties/manylinux-armv7l-image"
},
"manylinux-i686-image": {
"$ref": "#/properties/manylinux-i686-image"
},
@@ -527,6 +733,9 @@
"manylinux-pypy_x86_64-image": {
"$ref": "#/properties/manylinux-pypy_x86_64-image"
},
"manylinux-riscv64-image": {
"$ref": "#/properties/manylinux-riscv64-image"
},
"manylinux-s390x-image": {
"$ref": "#/properties/manylinux-s390x-image"
},
@@ -536,18 +745,30 @@
"musllinux-aarch64-image": {
"$ref": "#/properties/musllinux-aarch64-image"
},
"musllinux-armv7l-image": {
"$ref": "#/properties/musllinux-armv7l-image"
},
"musllinux-i686-image": {
"$ref": "#/properties/musllinux-i686-image"
},
"musllinux-ppc64le-image": {
"$ref": "#/properties/musllinux-ppc64le-image"
},
"musllinux-riscv64-image": {
"$ref": "#/properties/musllinux-riscv64-image"
},
"musllinux-s390x-image": {
"$ref": "#/properties/musllinux-s390x-image"
},
"musllinux-x86_64-image": {
"$ref": "#/properties/musllinux-x86_64-image"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"$ref": "#/properties/repair-wheel-command"
},
@@ -557,8 +778,20 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
}
@@ -600,6 +833,9 @@
"manylinux-aarch64-image": {
"$ref": "#/properties/manylinux-aarch64-image"
},
"manylinux-armv7l-image": {
"$ref": "#/properties/manylinux-armv7l-image"
},
"manylinux-i686-image": {
"$ref": "#/properties/manylinux-i686-image"
},
@@ -615,6 +851,9 @@
"manylinux-pypy_x86_64-image": {
"$ref": "#/properties/manylinux-pypy_x86_64-image"
},
"manylinux-riscv64-image": {
"$ref": "#/properties/manylinux-riscv64-image"
},
"manylinux-s390x-image": {
"$ref": "#/properties/manylinux-s390x-image"
},
@@ -624,18 +863,30 @@
"musllinux-aarch64-image": {
"$ref": "#/properties/musllinux-aarch64-image"
},
"musllinux-armv7l-image": {
"$ref": "#/properties/musllinux-armv7l-image"
},
"musllinux-i686-image": {
"$ref": "#/properties/musllinux-i686-image"
},
"musllinux-ppc64le-image": {
"$ref": "#/properties/musllinux-ppc64le-image"
},
"musllinux-riscv64-image": {
"$ref": "#/properties/musllinux-riscv64-image"
},
"musllinux-s390x-image": {
"$ref": "#/properties/musllinux-s390x-image"
},
"musllinux-x86_64-image": {
"$ref": "#/properties/musllinux-x86_64-image"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"description": "Execute a shell command to repair each built wheel.",
"oneOf": [
@@ -658,8 +909,20 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
},
@@ -694,6 +957,12 @@
"environment": {
"$ref": "#/properties/environment"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"$ref": "#/properties/repair-wheel-command"
},
@@ -703,8 +972,20 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
},
@@ -739,6 +1020,12 @@
"environment": {
"$ref": "#/properties/environment"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"description": "Execute a shell command to repair each built wheel.",
"oneOf": [
@@ -761,8 +1048,209 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
},
"pyodide": {
"type": "object",
"additionalProperties": false,
"properties": {
"archs": {
"$ref": "#/properties/archs"
},
"before-all": {
"$ref": "#/properties/before-all"
},
"before-build": {
"$ref": "#/properties/before-build"
},
"before-test": {
"$ref": "#/properties/before-test"
},
"build-frontend": {
"$ref": "#/properties/build-frontend"
},
"build-verbosity": {
"$ref": "#/properties/build-verbosity"
},
"config-settings": {
"$ref": "#/properties/config-settings"
},
"dependency-versions": {
"$ref": "#/properties/dependency-versions"
},
"environment": {
"$ref": "#/properties/environment"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"$ref": "#/properties/repair-wheel-command"
},
"test-command": {
"$ref": "#/properties/test-command"
},
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
},
"android": {
"type": "object",
"additionalProperties": false,
"properties": {
"archs": {
"$ref": "#/properties/archs"
},
"before-all": {
"$ref": "#/properties/before-all"
},
"before-build": {
"$ref": "#/properties/before-build"
},
"before-test": {
"$ref": "#/properties/before-test"
},
"build-frontend": {
"$ref": "#/properties/build-frontend"
},
"build-verbosity": {
"$ref": "#/properties/build-verbosity"
},
"config-settings": {
"$ref": "#/properties/config-settings"
},
"dependency-versions": {
"$ref": "#/properties/dependency-versions"
},
"environment": {
"$ref": "#/properties/environment"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"$ref": "#/properties/repair-wheel-command"
},
"test-command": {
"$ref": "#/properties/test-command"
},
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
},
"ios": {
"type": "object",
"additionalProperties": false,
"properties": {
"archs": {
"$ref": "#/properties/archs"
},
"before-all": {
"$ref": "#/properties/before-all"
},
"before-build": {
"$ref": "#/properties/before-build"
},
"before-test": {
"$ref": "#/properties/before-test"
},
"build-frontend": {
"$ref": "#/properties/build-frontend"
},
"build-verbosity": {
"$ref": "#/properties/build-verbosity"
},
"config-settings": {
"$ref": "#/properties/config-settings"
},
"dependency-versions": {
"$ref": "#/properties/dependency-versions"
},
"environment": {
"$ref": "#/properties/environment"
},
"xbuild-tools": {
"$ref": "#/properties/xbuild-tools"
},
"pyodide-version": {
"$ref": "#/properties/pyodide-version"
},
"repair-wheel-command": {
"$ref": "#/properties/repair-wheel-command"
},
"test-command": {
"$ref": "#/properties/test-command"
},
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
"test-requires": {
"$ref": "#/properties/test-requires"
},
"test-environment": {
"$ref": "#/properties/test-environment"
},
"test-runtime": {
"$ref": "#/properties/test-runtime"
}
}
}
@@ -0,0 +1,91 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
annotated-types==0.7.0
# via pydantic
auditwheel-emscripten==0.2.3
# via pyodide-build
build==1.2.2.post1
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.2.25
# via requests
charset-normalizer==3.4.6
# via requests
click==8.1.8
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
# pyodide-cli
distlib==0.4.0
# via virtualenv
filelock==3.25.2
# via
# python-discovery
# virtualenv
idna==3.11
# via requests
leb128==1.0.9
# via auditwheel-emscripten
markdown-it-py==4.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
packaging==26.0
# via
# auditwheel-emscripten
# build
# pyodide-build
# wheel
pip==26.0.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.4
# via
# pyodide-build
# python-discovery
# virtualenv
pydantic==2.12.5
# via
# pyodide-build
# pyodide-lock
pydantic-core==2.41.5
# via pydantic
pygments==2.19.2
# via rich
pyodide-build==0.33.0
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
# auditwheel-emscripten
# pyodide-build
pyodide-lock==0.1.2
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
# via virtualenv
requests==2.32.5
# via pyodide-build
rich==14.3.3
# via
# pyodide-build
# pyodide-cli
ruamel-yaml==0.19.1
# via pyodide-build
typing-extensions==4.15.0
# via
# pydantic
# pydantic-core
# typing-inspection
typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.2.0
# via
# build
# pyodide-build
wheel==0.46.3
# via
# auditwheel-emscripten
# pyodide-build
@@ -0,0 +1,91 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
annotated-types==0.7.0
# via pydantic
auditwheel-emscripten==0.2.3
# via pyodide-build
build==1.2.2.post1
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
certifi==2026.2.25
# via requests
charset-normalizer==3.4.6
# via requests
click==8.1.8
# via
# -r .nox/update_constraints/tmp/constraints-pyodide.in
# pyodide-build
# pyodide-cli
distlib==0.4.0
# via virtualenv
filelock==3.25.2
# via
# python-discovery
# virtualenv
idna==3.11
# via requests
leb128==1.0.9
# via auditwheel-emscripten
markdown-it-py==4.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
packaging==26.0
# via
# auditwheel-emscripten
# build
# pyodide-build
# wheel
pip==26.0.1
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
platformdirs==4.9.4
# via
# pyodide-build
# python-discovery
# virtualenv
pydantic==2.12.5
# via
# pyodide-build
# pyodide-lock
pydantic-core==2.41.5
# via pydantic
pygments==2.19.2
# via rich
pyodide-build==0.33.0
# via -r .nox/update_constraints/tmp/constraints-pyodide.in
pyodide-cli==0.5.0
# via
# auditwheel-emscripten
# pyodide-build
pyodide-lock==0.1.2
# via pyodide-build
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
# via virtualenv
requests==2.32.5
# via pyodide-build
rich==14.3.3
# via
# pyodide-build
# pyodide-cli
ruamel-yaml==0.19.1
# via pyodide-build
typing-extensions==4.15.0
# via
# pydantic
# pydantic-core
# typing-inspection
typing-inspection==0.4.2
# via pydantic
urllib3==2.6.3
# via requests
virtualenv==21.2.0
# via
# build
# pyodide-build
wheel==0.46.3
# via
# auditwheel-emscripten
# pyodide-build
@@ -1,34 +1,42 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
importlib-metadata==7.1.0
filelock==3.25.2
# via
# python-discovery
# virtualenv
importlib-metadata==9.0.0
# via build
macholib==1.16.3
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
platformdirs==4.9.4
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
# via virtualenv
pyproject-hooks==1.1.0
tomli==2.4.0
# via build
tomli==2.0.1
# via build
typing-extensions==4.11.0
# via delocate
virtualenv==20.26.2
typing-extensions==4.15.0
# via
# delocate
# virtualenv
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
zipp==3.18.2
zipp==3.23.0
# via importlib-metadata
@@ -1,28 +1,34 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
macholib==1.16.3
filelock==3.25.2
# via
# python-discovery
# virtualenv
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
# via virtualenv
pyproject-hooks==1.1.0
platformdirs==4.9.4
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
typing-extensions==4.11.0
python-discovery==1.2.0
# via virtualenv
typing-extensions==4.15.0
# via delocate
virtualenv==20.26.2
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
@@ -1,28 +1,34 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
macholib==1.16.3
filelock==3.25.2
# via
# python-discovery
# virtualenv
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
# via virtualenv
pyproject-hooks==1.1.0
platformdirs==4.9.4
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
typing-extensions==4.11.0
python-discovery==1.2.0
# via virtualenv
typing-extensions==4.15.0
# via delocate
virtualenv==20.26.2
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
@@ -1,28 +1,34 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
macholib==1.16.3
filelock==3.25.2
# via
# python-discovery
# virtualenv
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.1b1
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
# via virtualenv
pyproject-hooks==1.1.0
platformdirs==4.9.4
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
typing-extensions==4.11.0
python-discovery==1.2.0
# via virtualenv
typing-extensions==4.15.0
# via delocate
virtualenv==20.26.2
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
@@ -1,39 +1,34 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.1.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.12.2
# via virtualenv
importlib-metadata==6.7.0
filelock==3.25.2
# via
# build
# python-discovery
# virtualenv
macholib==1.16.3
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.0.0
# via virtualenv
pyproject-hooks==1.1.0
# via build
tomli==2.0.1
# via build
typing-extensions==4.7.1
platformdirs==4.9.4
# via
# delocate
# importlib-metadata
# platformdirs
virtualenv==20.26.2
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
# via virtualenv
typing-extensions==4.15.0
# via delocate
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
zipp==3.15.0
# via importlib-metadata
@@ -1,52 +0,0 @@
#
# This file is autogenerated by pip-compile with python 3.6
# To update, run:
#
# nox -s update_constraints-3.6
#
build==0.9.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.10.2
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
# via virtualenv
filelock==3.4.1
# via virtualenv
importlib-metadata==4.8.3
# via
# build
# pep517
# virtualenv
importlib-resources==5.4.0
# via virtualenv
packaging==21.3
# via build
pep517==0.13.1
# via build
platformdirs==2.4.0
# via virtualenv
pyparsing==3.1.1
# via packaging
tomli==1.2.3
# via
# build
# pep517
typing-extensions==4.1.1
# via
# delocate
# importlib-metadata
virtualenv==20.17.1
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via delocate
zipp==3.6.0
# via
# importlib-metadata
# importlib-resources
# pep517
# The following packages are considered to be unsafe in a requirements file:
pip==21.3.1
# via -r cibuildwheel/resources/constraints.in
setuptools==59.6.0
# via -r cibuildwheel/resources/constraints.in
+26 -18
View File
@@ -1,34 +1,42 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.2.2.post1
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.12.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
importlib-metadata==7.1.0
filelock==3.16.1
# via
# python-discovery
# virtualenv
importlib-metadata==8.5.0
# via build
macholib==1.16.3
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==25.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
platformdirs==4.3.6
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
# via virtualenv
pyproject-hooks==1.1.0
tomli==2.4.0
# via build
tomli==2.0.1
# via build
typing-extensions==4.11.0
# via delocate
virtualenv==20.26.2
typing-extensions==4.13.2
# via
# delocate
# virtualenv
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
zipp==3.18.2
zipp==3.20.2
# via importlib-metadata
+26 -18
View File
@@ -1,34 +1,42 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
importlib-metadata==7.1.0
filelock==3.19.1
# via
# python-discovery
# virtualenv
importlib-metadata==8.7.1
# via build
macholib==1.16.3
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
platformdirs==4.4.0
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
python-discovery==1.2.0
# via virtualenv
pyproject-hooks==1.1.0
tomli==2.4.0
# via build
tomli==2.0.1
# via build
typing-extensions==4.11.0
# via delocate
virtualenv==20.26.2
typing-extensions==4.15.0
# via
# delocate
# virtualenv
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
zipp==3.18.2
zipp==3.23.0
# via importlib-metadata
+1 -2
View File
@@ -1,5 +1,4 @@
pip>=24.1b1 ; python_version >= '3.13'
pip ; python_version < '3.13'
pip
build
delocate
virtualenv
+20 -14
View File
@@ -1,28 +1,34 @@
# This file was autogenerated by uv via the following command:
# nox -s update_constraints
altgraph==0.17.4
altgraph==0.17.5
# via macholib
build==1.2.1
build==1.4.0
# via -r cibuildwheel/resources/constraints.in
delocate==0.11.0
delocate==0.13.0
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.8
distlib==0.4.0
# via virtualenv
filelock==3.14.0
# via virtualenv
macholib==1.16.3
filelock==3.25.2
# via
# python-discovery
# virtualenv
macholib==1.16.4
# via delocate
packaging==24.0
packaging==26.0
# via
# build
# delocate
pip==24.0
pip==26.0.1
# via -r cibuildwheel/resources/constraints.in
platformdirs==4.2.2
# via virtualenv
pyproject-hooks==1.1.0
platformdirs==4.9.4
# via
# python-discovery
# virtualenv
pyproject-hooks==1.2.0
# via build
typing-extensions==4.11.0
python-discovery==1.2.0
# via virtualenv
typing-extensions==4.15.0
# via delocate
virtualenv==20.26.2
virtualenv==21.2.0
# via -r cibuildwheel/resources/constraints.in
+27 -9
View File
@@ -2,7 +2,7 @@
build = "*"
skip = ""
test-skip = ""
free-threaded-support = false
enable = []
archs = ["auto"]
build-frontend = "default"
@@ -14,29 +14,41 @@ build-verbosity = 0
before-all = ""
before-build = ""
# TOML doesn't support explicit NULLs; use ["\u0000"] as a sentinel value.
xbuild-tools = ["\u0000"]
repair-wheel-command = ""
test-command = ""
before-test = ""
test-sources = []
test-requires = []
test-extras = []
test-groups = []
test-environment = {}
test-runtime = {}
container-engine = "docker"
manylinux-x86_64-image = "manylinux2014"
manylinux-i686-image = "manylinux2014"
manylinux-aarch64-image = "manylinux2014"
manylinux-ppc64le-image = "manylinux2014"
manylinux-s390x-image = "manylinux2014"
manylinux-pypy_x86_64-image = "manylinux2014"
manylinux-pypy_i686-image = "manylinux2014"
manylinux-pypy_aarch64-image = "manylinux2014"
pyodide-version = ""
manylinux-x86_64-image = "manylinux_2_28"
manylinux-i686-image = "manylinux_2_28"
manylinux-aarch64-image = "manylinux_2_28"
manylinux-ppc64le-image = "manylinux_2_28"
manylinux-s390x-image = "manylinux_2_28"
manylinux-armv7l-image = "manylinux_2_31"
manylinux-riscv64-image = "manylinux_2_39"
manylinux-pypy_x86_64-image = "manylinux_2_28"
manylinux-pypy_i686-image = "manylinux_2_28"
manylinux-pypy_aarch64-image = "manylinux_2_28"
musllinux-x86_64-image = "musllinux_1_2"
musllinux-i686-image = "musllinux_1_2"
musllinux-aarch64-image = "musllinux_1_2"
musllinux-ppc64le-image = "musllinux_1_2"
musllinux-s390x-image = "musllinux_1_2"
musllinux-armv7l-image = "musllinux_1_2"
musllinux-riscv64-image = "musllinux_1_2"
[tool.cibuildwheel.linux]
@@ -46,3 +58,9 @@ repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}"
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
[tool.cibuildwheel.windows]
[tool.cibuildwheel.android]
[tool.cibuildwheel.ios]
[tool.cibuildwheel.pyodide]
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>org.python.Python.PythonTFramework-3.13</string>
</dict>
</array>
</plist>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>org.python.Python.PythonTFramework-3.14</string>
</dict>
</array>
</plist>
+2 -2
View File
@@ -26,14 +26,14 @@ STAT_0o775 = (
)
def main():
def main() -> None:
openssl_dir, openssl_cafile = os.path.split(ssl.get_default_verify_paths().openssl_cafile)
print(" -- pip install --upgrade certifi")
subprocess.check_call(
[sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]
)
import certifi
import certifi # noqa: PLC0415
# change working directory to the default SSL directory
os.chdir(openssl_dir)
+4
View File
@@ -0,0 +1,4 @@
url = "https://nodejs.org/dist/"
v24 = "v24.14.0"
v22 = "v22.22.1"
v20 = "v20.20.1"
+42 -42
View File
@@ -1,54 +1,54 @@
[x86_64]
manylinux1 = quay.io/pypa/manylinux1_x86_64:2024-04-29-76807b8
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-05-13-0983f6f
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-05-13-0983f6f
musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2024-05-13-0983f6f
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2024-05-13-0983f6f
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_x86_64:2026.03.20-1
[i686]
manylinux1 = quay.io/pypa/manylinux1_i686:2024-04-29-76807b8
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-05-13-0983f6f
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-05-13-0983f6f
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2024-05-13-0983f6f
[pypy_x86_64]
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2024-05-13-0983f6f
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-05-13-0983f6f
[pypy_i686]
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
manylinux2014 = quay.io/pypa/manylinux2014_i686:2024-05-13-0983f6f
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
manylinux2014 = quay.io/pypa/manylinux2014_i686:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_i686:2026.03.20-1
[aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024-05-13-0983f6f
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-05-13-0983f6f
musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2024-05-13-0983f6f
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2024-05-13-0983f6f
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_aarch64:2026.03.20-1
[ppc64le]
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2024-05-13-0983f6f
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-05-13-0983f6f
musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2024-05-13-0983f6f
musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2024-05-13-0983f6f
manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_ppc64le:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_ppc64le:2026.03.20-1
[s390x]
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2024-05-13-0983f6f
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-05-13-0983f6f
musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2024-05-13-0983f6f
musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2024-05-13-0983f6f
manylinux2014 = quay.io/pypa/manylinux2014_s390x:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_s390x:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_s390x:2026.03.20-1
[pypy_x86_64]
manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_x86_64:2026.03.20-1
[pypy_i686]
manylinux2014 = quay.io/pypa/manylinux2014_i686:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_i686:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_i686:2026.03.20-1
[pypy_aarch64]
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2024-05-13-0983f6f
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-05-13-0983f6f
manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2026.03.20-1
manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2026.03.20-1
manylinux_2_34 = quay.io/pypa/manylinux_2_34_aarch64:2026.03.20-1
[armv7l]
manylinux_2_31 = quay.io/pypa/manylinux_2_31_armv7l:2026.03.20-1
manylinux_2_35 = quay.io/pypa/manylinux_2_35_armv7l:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_armv7l:2026.03.20-1
[riscv64]
manylinux_2_39 = quay.io/pypa/manylinux_2_39_riscv64:2026.03.20-1
musllinux_1_2 = quay.io/pypa/musllinux_1_2_riscv64:2026.03.20-1
@@ -0,0 +1,725 @@
{
"releases": [
{
"tag": "20260320",
"assets": [
{
"name": "cpython-3.10.20+20260320-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-aarch64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-aarch64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-aarch64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-aarch64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-i686-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-riscv64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-s390x-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.10.20+20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.10.20%2B20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-aarch64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-aarch64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-aarch64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-aarch64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-aarch64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-i686-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-riscv64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-s390x-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.11.15+20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.11.15%2B20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-aarch64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-aarch64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-aarch64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-aarch64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-aarch64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-i686-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-riscv64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-s390x-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.12.13+20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.12.13%2B20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-apple-darwin-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-apple-darwin-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-aarch64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-aarch64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-armv7-unknown-linux-gnueabi-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-armv7-unknown-linux-gnueabi-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-armv7-unknown-linux-gnueabihf-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-armv7-unknown-linux-gnueabihf-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-i686-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-i686-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-i686-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-ppc64le-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-ppc64le-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-riscv64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-riscv64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-riscv64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-s390x-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-s390x-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-s390x-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-apple-darwin-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-apple-darwin-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v2-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v2-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v2-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v2-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v3-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v3-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v3-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v3-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v4-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v4-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v4-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v4-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.13.12+20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.13.12%2B20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-apple-darwin-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-apple-darwin-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-aarch64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-aarch64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-armv7-unknown-linux-gnueabi-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-armv7-unknown-linux-gnueabi-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-armv7-unknown-linux-gnueabihf-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-armv7-unknown-linux-gnueabihf-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-i686-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-i686-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-i686-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-ppc64le-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-ppc64le-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-riscv64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-riscv64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-riscv64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-s390x-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-s390x-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-s390x-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-apple-darwin-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-apple-darwin-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v2-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v2-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v2-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v2-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v3-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v3-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v3-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v3-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v4-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v4-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v4-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v4-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.14.3+20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.14.3%2B20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-apple-darwin-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-apple-darwin-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-aarch64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-aarch64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-armv7-unknown-linux-gnueabi-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-armv7-unknown-linux-gnueabi-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-armv7-unknown-linux-gnueabi-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-armv7-unknown-linux-gnueabihf-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-armv7-unknown-linux-gnueabihf-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-armv7-unknown-linux-gnueabihf-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-i686-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-i686-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-i686-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-i686-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-ppc64le-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-ppc64le-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-ppc64le-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-riscv64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-riscv64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-riscv64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-riscv64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-s390x-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-s390x-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-s390x-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-s390x-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-apple-darwin-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-apple-darwin-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-apple-darwin-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-apple-darwin-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-pc-windows-msvc-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-pc-windows-msvc-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-pc-windows-msvc-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-pc-windows-msvc-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v2-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v2-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v2-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v2-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v2-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v2-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v3-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v3-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v3-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v3-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v3-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v3-unknown-linux-musl-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v4-unknown-linux-gnu-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v4-unknown-linux-gnu-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v4-unknown-linux-gnu-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v4-unknown-linux-musl-freethreaded-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v4-unknown-linux-musl-freethreaded-install_only.tar.gz"
},
{
"name": "cpython-3.15.0a7+20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260320/cpython-3.15.0a7%2B20260320-x86_64_v4-unknown-linux-musl-install_only.tar.gz"
}
]
}
]
}
+29 -10
View File
@@ -1,17 +1,36 @@
# this file is copied to the testing cwd, to raise the below error message if
# pytest/unittest is run from there.
import sys
import unittest
from typing import NoReturn
class TestStringMethods(unittest.TestCase):
def test_fail(self):
self.fail(
"cibuildwheel executes tests from a different working directory to "
"your project. This ensures only your wheel is imported, preventing "
"Python from accessing files that haven't been packaged into the "
"wheel. Please specify a path to your tests when invoking pytest "
"using the {project} placeholder, e.g. `pytest {project}` or "
"`pytest {project}/tests`. cibuildwheel will replace {project} with "
"the path to your project."
)
def test_fail(self) -> NoReturn:
if sys.platform in ["android", "ios"]:
msg = (
"You tried to run tests from the testbed app's working "
"directory, without specifying `test-sources`. "
"On this platform, you must copy your test files to the testbed app by "
"setting the `test-sources` option in your cibuildwheel "
"configuration."
)
else:
msg = (
"cibuildwheel executes tests from a different working directory to "
"your project. This ensures only your wheel is imported, preventing "
"Python from accessing files that haven't been packaged into the "
"wheel. "
"\n\n"
"Please specify a path to your tests when invoking pytest "
"using the {project} placeholder, e.g. `pytest {project}` or "
"`pytest {project}/tests`. cibuildwheel will replace {project} with "
"the path to your project. "
"\n\n"
"Alternatively, you can specify your test files using the "
"`test-sources` option, and cibuildwheel will copy them to the "
"working directory for testing."
)
self.fail(msg)
+1 -2
View File
@@ -1,2 +1 @@
py36 = { version = "20.21.1", url = "https://github.com/pypa/get-virtualenv/blob/20.21.1/public/virtualenv.pyz?raw=true" }
default = { version = "20.26.2", url = "https://github.com/pypa/get-virtualenv/blob/20.26.2/public/virtualenv.pyz?raw=true" }
default = { version = "21.2.0", url = "https://github.com/pypa/get-virtualenv/releases/download/21.2.0/virtualenv.pyz" }
+2 -5
View File
@@ -1,15 +1,12 @@
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
DIR = Path(__file__).parent.resolve()
from cibuildwheel.util import resources
def get_schema(tool_name: str = "cibuildwheel") -> dict[str, Any]:
"Get the stored complete schema for cibuildwheel settings."
assert tool_name == "cibuildwheel", "Only cibuildwheel is supported."
with DIR.joinpath("resources/cibuildwheel.schema.json").open(encoding="utf-8") as f:
with resources.CIBUILDWHEEL_SCHEMA.open(encoding="utf-8") as f:
return json.load(f) # type: ignore[no-any-return]
+139
View File
@@ -0,0 +1,139 @@
import dataclasses
import itertools
from enum import StrEnum
from fnmatch import fnmatch
from typing import Self
import bracex
from packaging.specifiers import SpecifierSet
from packaging.version import Version
def selector_matches(patterns: str, string: str) -> bool:
"""
Returns True if `string` is matched by any of the wildcard patterns in
`patterns`.
Matching is according to fnmatch, but with shell-like curly brace
expansion. For example, 'cp{36,37}-*' would match either of 'cp36-*' or
'cp37-*'.
"""
patterns_list = patterns.split()
expanded_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in patterns_list)
return any(fnmatch(string, pat) for pat in expanded_patterns)
class EnableGroup(StrEnum):
"""
Groups of build selectors that are not enabled by default.
"""
CPythonFreeThreading = "cpython-freethreading"
CPythonPrerelease = "cpython-prerelease"
GraalPy = "graalpy"
PyPy = "pypy"
PyPyEoL = "pypy-eol"
PyodidePrerelease = "pyodide-prerelease"
@classmethod
def all_groups(cls) -> frozenset[Self]:
# see https://github.com/python/mypy/issues/20434 for the reason to type: ignore[arg-type]
return frozenset(set(cls) - {cls.CPythonFreeThreading}) # type: ignore[arg-type]
@classmethod
def parse_option_value(cls, value: str) -> frozenset[Self]:
"""
Parses a string of space-separated values into a set of EnableGroup
members. The string may contain group names or "all".
"""
result = set()
for group in value.strip().split():
if group == "all":
return cls.all_groups()
try:
result.add(cls(group))
except ValueError:
msg = f"Unknown enable group: {group}"
raise ValueError(msg) from None
return frozenset(result)
@dataclasses.dataclass(frozen=True, kw_only=True)
class BuildSelector:
"""
This class holds a set of build/skip patterns. You call an instance with a
build identifier, and it returns True if that identifier should be
included. Only call this on valid identifiers, ones that have at least 2
numeric digits before the first dash.
"""
build_config: str
skip_config: str
requires_python: SpecifierSet | None = None
enable: frozenset[EnableGroup] = frozenset()
def __call__(self, build_id: str) -> bool:
# Filter build selectors by python_requires if set
if self.requires_python is not None:
py_ver_str = build_id.split("-", maxsplit=1)[0].split("_", maxsplit=1)[0]
py_ver_str = py_ver_str.removesuffix("t")
major = int(py_ver_str[2])
minor = int(py_ver_str[3:])
version = Version(f"{major}.{minor}.99")
if not self.requires_python.contains(version):
return False
# filter out groups that are not enabled
if EnableGroup.CPythonFreeThreading not in self.enable and fnmatch(build_id, "cp313t-*"):
return False
if EnableGroup.CPythonPrerelease not in self.enable and fnmatch(build_id, "cp315*"):
return False
is_pypy_eol = fnmatch(build_id, "pp3?-*") or fnmatch(build_id, "pp310-*")
is_pypy = fnmatch(build_id, "pp*") and not is_pypy_eol
if EnableGroup.PyPy not in self.enable and is_pypy:
return False
if EnableGroup.PyPyEoL not in self.enable and is_pypy_eol:
return False
if EnableGroup.GraalPy not in self.enable and fnmatch(build_id, "gp*"):
return False
# TODO: Re-enable this when we have Pyodide prereleases again (e.g., 0.29.0a1+)
# Python 3.13 support became stable in Pyodide 0.28.0, so it no longer needs a prerelease
# flag.
# Also update Pyodide tests in unit_test/build_selector_test.py accordingly.
# When re-enabling, update the pattern to match the experimental Python version in case
# it is bumped to Python 3.14 (likely cp314-pyodide_* but could remain as 3.13 as well).
# This depends on the CPython version being used in the Pyodide runtime at the time.
# if EnableGroup.PyodidePrerelease not in self.enable and fnmatch(
# build_id, "cp313-pyodide_*"
# ):
# return False
should_build = selector_matches(self.build_config, build_id)
should_skip = selector_matches(self.skip_config, build_id)
return should_build and not should_skip
def options_summary(self) -> dict[str, str | list[str]]:
return {
"build_config": self.build_config,
"skip_config": self.skip_config,
"requires_python": str(self.requires_python),
"enable": sorted(group.value for group in self.enable),
}
@dataclasses.dataclass(frozen=True, kw_only=True)
class TestSelector:
"""
A build selector that can only skip tests according to a skip pattern.
"""
skip_config: str
def __call__(self, build_id: str) -> bool:
should_skip = selector_matches(self.skip_config, build_id)
return not should_skip
def options_summary(self) -> dict[str, str]:
return {"skip_config": self.skip_config}
+4 -14
View File
@@ -1,29 +1,19 @@
from __future__ import annotations
import os
import subprocess
import typing
from typing import Final, Literal, Protocol, Union
from typing import Final, Literal, Protocol
__all__ = (
"PLATFORMS",
"PathOrStr",
"PlatformName",
"PLATFORMS",
"PopenBytes",
)
if typing.TYPE_CHECKING:
PopenBytes = subprocess.Popen[bytes]
PathOrStr = Union[str, os.PathLike[str]]
else:
PopenBytes = subprocess.Popen
PathOrStr = Union[str, "os.PathLike[str]"]
PathOrStr = str | os.PathLike[str]
PlatformName = Literal["linux", "macos", "windows"]
PLATFORMS: Final[set[PlatformName]] = {"linux", "macos", "windows"}
PlatformName = Literal["linux", "macos", "windows", "pyodide", "android", "ios"]
PLATFORMS: Final[frozenset[PlatformName]] = frozenset(typing.get_args(PlatformName))
class GenericPythonConfiguration(Protocol):
-784
View File
@@ -1,784 +0,0 @@
from __future__ import annotations
import contextlib
import fnmatch
import itertools
import os
import re
import shlex
import ssl
import subprocess
import sys
import textwrap
import time
import typing
import urllib.request
from collections import defaultdict
from collections.abc import Generator, Iterable, Mapping, Sequence
from dataclasses import dataclass
from enum import Enum
from functools import cached_property, lru_cache
from pathlib import Path, PurePath
from time import sleep
from typing import Any, ClassVar, Final, Literal, TextIO, TypeVar
import bracex
import certifi
from filelock import FileLock
from packaging.requirements import InvalidRequirement, Requirement
from packaging.specifiers import SpecifierSet
from packaging.utils import parse_wheel_filename
from packaging.version import Version
from platformdirs import user_cache_path
from ._compat import tomllib
from .typing import PathOrStr, PlatformName
__all__ = [
"resources_dir",
"MANYLINUX_ARCHS",
"call",
"shell",
"find_compatible_wheel",
"format_safe",
"prepare_command",
"get_build_verbosity_extra_flags",
"read_python_configs",
"selector_matches",
"strtobool",
"cached_property",
"chdir",
"split_config_settings",
]
resources_dir: Final[Path] = Path(__file__).parent / "resources"
install_certifi_script: Final[Path] = resources_dir / "install_certifi.py"
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64",
"i686",
"pypy_x86_64",
"aarch64",
"ppc64le",
"s390x",
"pypy_aarch64",
"pypy_i686",
)
MUSLLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64",
"i686",
"aarch64",
"ppc64le",
"s390x",
)
DEFAULT_CIBW_CACHE_PATH: Final[Path] = user_cache_path(appname="cibuildwheel", appauthor="pypa")
CIBW_CACHE_PATH: Final[Path] = Path(
os.environ.get("CIBW_CACHE_PATH", DEFAULT_CIBW_CACHE_PATH)
).resolve()
IS_WIN: Final[bool] = sys.platform.startswith("win")
@typing.overload
def call(
*args: PathOrStr,
env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: Literal[False] = ...,
) -> None: ...
@typing.overload
def call(
*args: PathOrStr,
env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: Literal[True],
) -> str: ...
def call(
*args: PathOrStr,
env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: bool = False,
) -> str | None:
"""
Run subprocess.run, but print the commands first. Takes the commands as
*args. Uses shell=True on Windows due to a bug. Also converts to
Paths to strings, due to Windows behavior at least on older Pythons.
https://bugs.python.org/issue8557
"""
args_ = [str(arg) for arg in args]
# print the command executing for the logs
print("+ " + " ".join(shlex.quote(a) for a in args_))
kwargs: dict[str, Any] = {}
if capture_stdout:
kwargs["universal_newlines"] = True
kwargs["stdout"] = subprocess.PIPE
result = subprocess.run(args_, check=True, shell=IS_WIN, env=env, cwd=cwd, **kwargs)
if not capture_stdout:
return None
return typing.cast(str, result.stdout)
def shell(
*commands: str, env: Mapping[str, str] | None = None, cwd: PathOrStr | None = None
) -> None:
command = " ".join(commands)
print(f"+ {command}")
subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)
def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str:
"""
Works similarly to `template.format(**kwargs)`, except that unmatched
fields in `template` are passed through untouched.
>>> format_safe('{a} {b}', a='123')
'123 {b}'
>>> format_safe('{a} {b[4]:3f}', a='123')
'123 {b[4]:3f}'
To avoid variable expansion, precede with a single backslash e.g.
>>> format_safe('\\{a} {b}', a='123')
'{a} {b}'
"""
result = template
for key, value in kwargs.items():
find_pattern = re.compile(
rf"""
(?<!\#) # don't match if preceded by a hash
{{ # literal open curly bracket
{re.escape(key)} # the field name
}} # literal close curly bracket
""",
re.VERBOSE,
)
result = re.sub(
pattern=find_pattern,
repl=str(value).replace("\\", r"\\"),
string=result,
)
# transform escaped sequences into their literal equivalents
result = result.replace(f"#{{{key}}}", f"{{{key}}}")
return result
def prepare_command(command: str, **kwargs: PathOrStr) -> str:
"""
Preprocesses a command by expanding variables like {python}.
For example, used in the test_command option to specify the path to the
project's root. Unmatched syntax will mostly be allowed through.
"""
return format_safe(command, python="python", pip="pip", **kwargs)
def get_build_verbosity_extra_flags(level: int) -> list[str]:
if level > 0:
return ["-" + level * "v"]
elif level < 0:
return ["-" + -level * "q"]
else:
return []
def split_config_settings(config_settings: str, frontend: Literal["pip", "build"]) -> list[str]:
config_settings_list = shlex.split(config_settings)
s = "s" if frontend == "pip" else ""
return [f"--config-setting{s}={setting}" for setting in config_settings_list]
def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
input_file = resources_dir / "build-platforms.toml"
with input_file.open("rb") as f:
loaded_file = tomllib.load(f)
results: list[dict[str, str]] = list(loaded_file[config]["python_configurations"])
return results
def selector_matches(patterns: str, string: str) -> bool:
"""
Returns True if `string` is matched by any of the wildcard patterns in
`patterns`.
Matching is according to fnmatch, but with shell-like curly brace
expansion. For example, 'cp{36,37}-*' would match either of 'cp36-*' or
'cp37-*'.
"""
patterns_list = patterns.split()
expanded_patterns = itertools.chain.from_iterable(bracex.expand(p) for p in patterns_list)
return any(fnmatch.fnmatch(string, pat) for pat in expanded_patterns)
# Once we require Python 3.10+, we can add kw_only=True
@dataclass(frozen=True)
class BuildSelector:
"""
This class holds a set of build/skip patterns. You call an instance with a
build identifier, and it returns True if that identifier should be
included. Only call this on valid identifiers, ones that have at least 2
numeric digits before the first dash.
"""
build_config: str
skip_config: str
requires_python: SpecifierSet | None = None
# a pattern that skips prerelease versions, when include_prereleases is False.
PRERELEASE_SKIP: ClassVar[str] = "cp313-* cp313t-*"
prerelease_pythons: bool = False
free_threaded_support: bool = False
def __call__(self, build_id: str) -> bool:
# Filter build selectors by python_requires if set
if self.requires_python is not None:
py_ver_str = build_id.split("-")[0]
if py_ver_str.endswith("t"):
py_ver_str = py_ver_str[:-1]
major = int(py_ver_str[2])
minor = int(py_ver_str[3:])
version = Version(f"{major}.{minor}.99")
if not self.requires_python.contains(version):
return False
# filter out the prerelease pythons if self.prerelease_pythons is False
if not self.prerelease_pythons and selector_matches(self.PRERELEASE_SKIP, build_id):
return False
# filter out free threaded pythons if self.free_threaded_support is False
if not self.free_threaded_support and selector_matches("*t-*", build_id):
return False
should_build = selector_matches(self.build_config, build_id)
should_skip = selector_matches(self.skip_config, build_id)
return should_build and not should_skip
def options_summary(self) -> Any:
return {
"build_config": self.build_config,
"skip_config": self.skip_config,
"requires_python": str(self.requires_python),
"prerelease_pythons": self.prerelease_pythons,
"free_threaded_support": self.free_threaded_support,
}
@dataclass(frozen=True)
class TestSelector:
"""
A build selector that can only skip tests according to a skip pattern.
"""
skip_config: str
def __call__(self, build_id: str) -> bool:
should_skip = selector_matches(self.skip_config, build_id)
return not should_skip
def options_summary(self) -> Any:
return {"skip_config": self.skip_config}
# Taken from https://stackoverflow.com/a/107717
class Unbuffered:
def __init__(self, stream: TextIO) -> None:
self.stream = stream
def write(self, data: str) -> None:
self.stream.write(data)
self.stream.flush()
def writelines(self, data: Iterable[str]) -> None:
self.stream.writelines(data)
self.stream.flush()
def __getattr__(self, attr: str) -> Any:
return getattr(self.stream, attr)
def download(url: str, dest: Path) -> None:
print(f"+ Download {url} to {dest}")
dest_dir = dest.parent
if not dest_dir.exists():
dest_dir.mkdir(parents=True)
# we've had issues when relying on the host OS' CA certificates on Windows,
# so we use certifi (this sounds odd but requests also does this by default)
cafile = os.environ.get("SSL_CERT_FILE", certifi.where())
context = ssl.create_default_context(cafile=cafile)
repeat_num = 3
for i in range(repeat_num):
try:
with urllib.request.urlopen(url, context=context) as response:
dest.write_bytes(response.read())
return
except OSError:
if i == repeat_num - 1:
raise
sleep(3)
class DependencyConstraints:
def __init__(self, base_file_path: Path):
assert base_file_path.exists()
self.base_file_path = base_file_path.resolve()
@staticmethod
def with_defaults() -> DependencyConstraints:
return DependencyConstraints(base_file_path=resources_dir / "constraints.txt")
def get_for_python_version(self, version: str) -> Path:
version_parts = version.split(".")
# try to find a version-specific dependency file e.g. if
# ./constraints.txt is the base, look for ./constraints-python36.txt
specific_stem = self.base_file_path.stem + f"-python{version_parts[0]}{version_parts[1]}"
specific_name = specific_stem + self.base_file_path.suffix
specific_file_path = self.base_file_path.with_name(specific_name)
if specific_file_path.exists():
return specific_file_path
else:
return self.base_file_path
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.base_file_path!r})"
def __eq__(self, o: object) -> bool:
if not isinstance(o, DependencyConstraints):
return False
return self.base_file_path == o.base_file_path
def options_summary(self) -> Any:
if self == DependencyConstraints.with_defaults():
return "pinned"
else:
return self.base_file_path.name
BuildFrontendName = Literal["pip", "build"]
@dataclass(frozen=True)
class BuildFrontendConfig:
name: BuildFrontendName
args: Sequence[str] = ()
@staticmethod
def from_config_string(config_string: str) -> BuildFrontendConfig:
config_dict = parse_key_value_string(config_string, ["name"], ["args"])
name = " ".join(config_dict["name"])
if name not in {"pip", "build"}:
msg = f"Unrecognised build frontend {name}, only 'pip' and 'build' are supported"
raise ValueError(msg)
name = typing.cast(BuildFrontendName, name)
args = config_dict.get("args") or []
return BuildFrontendConfig(name=name, args=args)
def options_summary(self) -> str | dict[str, str]:
if not self.args:
return self.name
else:
return {"name": self.name, "args": repr(self.args)}
class NonPlatformWheelError(Exception):
def __init__(self) -> None:
message = textwrap.dedent(
"""
cibuildwheel: Build failed because a pure Python wheel was generated.
If you intend to build a pure-Python wheel, you don't need cibuildwheel - use
`pip wheel -w DEST_DIR .` instead.
If you expected a platform wheel, check your project configuration, or run
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
"""
)
super().__init__(message)
class AlreadyBuiltWheelError(Exception):
def __init__(self, wheel_name: str) -> None:
message = textwrap.dedent(
f"""
cibuildwheel: Build failed because a wheel named {wheel_name} was already generated in the current run.
If you expected another wheel to be generated, check your project configuration, or run
cibuildwheel with CIBW_BUILD_VERBOSITY=1 to view build logs.
"""
)
super().__init__(message)
def strtobool(val: str) -> bool:
return val.lower() in {"y", "yes", "t", "true", "on", "1"}
class CIProvider(Enum):
travis_ci = "travis"
appveyor = "appveyor"
circle_ci = "circle_ci"
azure_pipelines = "azure_pipelines"
github_actions = "github_actions"
gitlab = "gitlab"
cirrus_ci = "cirrus_ci"
other = "other"
def detect_ci_provider() -> CIProvider | None:
if "TRAVIS" in os.environ:
return CIProvider.travis_ci
elif "APPVEYOR" in os.environ:
return CIProvider.appveyor
elif "CIRCLECI" in os.environ:
return CIProvider.circle_ci
elif "AZURE_HTTP_USER_AGENT" in os.environ:
return CIProvider.azure_pipelines
elif "GITHUB_ACTIONS" in os.environ:
return CIProvider.github_actions
elif "GITLAB_CI" in os.environ:
return CIProvider.gitlab
elif "CIRRUS_CI" in os.environ:
return CIProvider.cirrus_ci
elif strtobool(os.environ.get("CI", "false")):
return CIProvider.other
else:
return None
def unwrap(text: str) -> str:
"""
Unwraps multi-line text to a single line
"""
# remove initial line indent
text = textwrap.dedent(text)
# remove leading/trailing whitespace
text = text.strip()
# remove consecutive whitespace
return re.sub(r"\s+", " ", text)
@dataclass(frozen=True)
class FileReport:
name: str
size: str
@contextlib.contextmanager
def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
"""
Prints the new items in a directory upon exiting. The message to display
can include {n} for number of wheels, {s} for total number of seconds,
and/or {m} for total number of minutes. Does not print anything if this
exits via exception.
"""
start_time = time.time()
existing_contents = set(output_dir.iterdir())
yield
final_contents = set(output_dir.iterdir())
new_contents = [
FileReport(wheel.name, f"{(wheel.stat().st_size + 1023) // 1024:,d}")
for wheel in final_contents - existing_contents
]
if not new_contents:
return
max_name_len = max(len(f.name) for f in new_contents)
max_size_len = max(len(f.size) for f in new_contents)
n = len(new_contents)
s = time.time() - start_time
m = s / 60
print(
msg.format(n=n, s=s, m=m),
*sorted(
f" {f.name:<{max_name_len}s} {f.size:>{max_size_len}s} kB" for f in new_contents
),
sep="\n",
)
def get_pip_version(env: Mapping[str, str]) -> str:
versions_output_text = call(
"python", "-m", "pip", "freeze", "--all", capture_stdout=True, env=env
)
(pip_version,) = (
version[5:]
for version in versions_output_text.strip().splitlines()
if version.startswith("pip==")
)
return pip_version
@lru_cache(maxsize=None)
def _ensure_virtualenv(version: str) -> Path:
version_parts = version.split(".")
key = f"py{version_parts[0]}{version_parts[1]}"
input_file = resources_dir / "virtualenv.toml"
with input_file.open("rb") as f:
loaded_file = tomllib.load(f)
configuration = loaded_file.get(key, loaded_file["default"])
version = str(configuration["version"])
url = str(configuration["url"])
path = CIBW_CACHE_PATH / f"virtualenv-{version}.pyz"
with FileLock(str(path) + ".lock"):
if not path.exists():
download(url, path)
return path
def _parse_constraints_for_virtualenv(
seed_packages: list[str],
dependency_constraint_flags: Sequence[PathOrStr],
) -> dict[str, str]:
"""
Parses the constraints file referenced by `dependency_constraint_flags` and returns a dict where
the key is the package name, and the value is the constraint version.
If a package version cannot be found, its value is "embed" meaning that virtualenv will install
its bundled version, already available locally.
The function does not try to be too smart and just handles basic constraints.
If it can't get an exact version, the real constraint will be handled by the
{macos|windows}.setup_python function.
"""
assert len(dependency_constraint_flags) in {0, 2}
# only seed pip if other seed packages do not appear in a constraint file
constraints_dict = {"pip": "embed"}
if len(dependency_constraint_flags) == 2:
assert dependency_constraint_flags[0] == "-c"
constraint_path = Path(dependency_constraint_flags[1])
assert constraint_path.exists()
with constraint_path.open(encoding="utf-8") as constraint_file:
for line_ in constraint_file:
line = line_.strip()
if not line:
continue
if line.startswith("#"):
continue
try:
requirement = Requirement(line)
package = requirement.name
if (
package not in seed_packages
or requirement.url is not None
or requirement.marker is not None
or len(requirement.extras) != 0
or len(requirement.specifier) != 1
):
continue
specifier = next(iter(requirement.specifier))
if specifier.operator != "==":
continue
constraints_dict[package] = specifier.version
except InvalidRequirement:
continue
return constraints_dict
def virtualenv(
version: str, python: Path, venv_path: Path, dependency_constraint_flags: Sequence[PathOrStr]
) -> dict[str, str]:
assert python.exists()
virtualenv_app = _ensure_virtualenv(version)
allowed_seed_packages = ["pip", "setuptools", "wheel"]
constraints = _parse_constraints_for_virtualenv(
allowed_seed_packages, dependency_constraint_flags
)
additional_flags: list[str] = []
for package in allowed_seed_packages:
if package in constraints:
additional_flags.append(f"--{package}={constraints[package]}")
else:
additional_flags.append(f"--no-{package}")
# Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
# environment. The initial cost is a bit higher but reusing is much faster.
# Windows does not always allow symlinks so just disabling for now.
# Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
# version of pip that will end-up installed.
# c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
if (
not IS_WIN
and constraints["pip"] != "embed"
and Version(constraints["pip"]) >= Version("19.3")
):
additional_flags.append("--symlink-app-data")
call(
sys.executable,
"-sS", # just the stdlib, https://github.com/pypa/virtualenv/issues/2133#issuecomment-1003710125
virtualenv_app,
"--activators=",
"--no-periodic-update",
*additional_flags,
"--python",
python,
venv_path,
)
paths = [str(venv_path), str(venv_path / "Scripts")] if IS_WIN else [str(venv_path / "bin")]
env = os.environ.copy()
env["PATH"] = os.pathsep.join([*paths, env["PATH"]])
return env
T = TypeVar("T", bound=PurePath)
def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
"""
Finds a wheel with an abi3 or a none ABI tag in `wheels` compatible with the Python interpreter
specified by `identifier` that is previously built.
"""
interpreter, platform = identifier.split("-")
free_threaded = interpreter.endswith("t")
if free_threaded:
interpreter = interpreter[:-1]
for wheel in wheels:
_, _, _, tags = parse_wheel_filename(wheel.name)
for tag in tags:
if tag.abi == "abi3" and not free_threaded:
# ABI3 wheels must start with cp3 for impl and tag
if not (interpreter.startswith("cp3") and tag.interpreter.startswith("cp3")):
continue
elif tag.abi == "none":
# CPythonless wheels must include py3 tag
if tag.interpreter[:3] != "py3":
continue
else:
# Other types of wheels are not detected, this is looking for previously built wheels.
continue
if tag.interpreter != "py3" and int(tag.interpreter[3:]) > int(interpreter[3:]):
# If a minor version number is given, it has to be lower than the current one.
continue
if platform.startswith(("manylinux", "musllinux", "macosx")):
# Linux, macOS require the beginning and ending match (macos/manylinux version doesn't need to)
os_, arch = platform.split("_", 1)
if not tag.platform.startswith(os_):
continue
if not tag.platform.endswith(f"_{arch}"):
continue
else:
# Windows should exactly match
if tag.platform != platform:
continue
# If all the filters above pass, then the wheel is a previously built compatible wheel.
return wheel
return None
# Can be replaced by contextlib.chdir in Python 3.11
@contextlib.contextmanager
def chdir(new_path: Path | str) -> Generator[None, None, None]:
"""Non thread-safe context manager to change the current working directory."""
cwd = os.getcwd()
try:
os.chdir(new_path)
yield
finally:
os.chdir(cwd)
def fix_ansi_codes_for_github_actions(text: str) -> str:
"""
Github Actions forgets the current ANSI style on every new line. This
function repeats the current ANSI style on every new line.
"""
ansi_code_regex = re.compile(r"(\033\[[0-9;]*m)")
ansi_codes: list[str] = []
output = ""
for line in text.splitlines(keepends=True):
# add the current ANSI codes to the beginning of the line
output += "".join(ansi_codes) + line
# split the line at each ANSI code
parts = ansi_code_regex.split(line)
# if there are any ANSI codes, save them
if len(parts) > 1:
# iterate over the ANSI codes in this line
for code in parts[1::2]:
if code == "\033[0m":
# reset the list of ANSI codes when the clear code is found
ansi_codes = []
else:
ansi_codes.append(code)
return output
def parse_key_value_string(
key_value_string: str,
positional_arg_names: Sequence[str] | None = None,
kw_arg_names: Sequence[str] | None = None,
) -> dict[str, list[str]]:
"""
Parses a string like "docker; create_args: --some-option=value another-option"
"""
if positional_arg_names is None:
positional_arg_names = []
if kw_arg_names is None:
kw_arg_names = []
all_field_names = [*positional_arg_names, *kw_arg_names]
shlexer = shlex.shlex(key_value_string, posix=True, punctuation_chars=";")
shlexer.commenters = ""
shlexer.whitespace_split = True
parts = list(shlexer)
# parts now looks like
# ['docker', ';', 'create_args:', '--some-option=value', 'another-option']
# split by semicolon
fields = [list(group) for k, group in itertools.groupby(parts, lambda x: x == ";") if not k]
result: defaultdict[str, list[str]] = defaultdict(list)
for field_i, field in enumerate(fields):
# check to see if the option name is specified
field_name, sep, first_value = field[0].partition(":")
if sep:
if field_name not in all_field_names:
msg = f"Failed to parse {key_value_string!r}. Unknown field name {field_name!r}"
raise ValueError(msg)
values = ([first_value] if first_value else []) + field[1:]
else:
try:
field_name = positional_arg_names[field_i]
except IndexError:
msg = f"Failed to parse {key_value_string!r}. Too many positional arguments - expected a maximum of {len(positional_arg_names)}"
raise ValueError(msg) from None
values = field
result[field_name] += values
return dict(result)
View File
+98
View File
@@ -0,0 +1,98 @@
import os
import shlex
import shutil
import subprocess
import sys
import typing
from collections.abc import Iterator, Mapping
from typing import Final, Literal
from cibuildwheel.errors import FatalError
from cibuildwheel.typing import PathOrStr
_IS_WIN: Final[bool] = sys.platform.startswith("win")
@typing.overload
def call(
*args: PathOrStr,
env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: Literal[False] = ...,
) -> None: ...
@typing.overload
def call(
*args: PathOrStr,
env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: Literal[True],
) -> str: ...
def call(
*args: PathOrStr,
env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: bool = False,
) -> str | None:
"""
Run subprocess.run, but print the commands first. Takes the commands as
*args. Uses shell=True on Windows due to a bug. Also converts to
Paths to strings, due to Windows behavior at least on older Pythons.
https://bugs.python.org/issue8557
"""
args_ = [str(arg) for arg in args]
# print the command executing for the logs
print("+ " + " ".join(shlex.quote(a) for a in args_))
# workaround platform behaviour differences outlined
# in https://github.com/python/cpython/issues/52803
path_env = env if env is not None else os.environ
path = path_env.get("PATH", None)
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:
return None
sys.stderr.write(result.stderr)
return typing.cast("str", result.stdout)
def shell(
*commands: str, env: Mapping[str, str] | None = None, cwd: PathOrStr | None = None
) -> None:
command = " ".join(commands)
print(f"+ {command}")
subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)
def split_command(lst: list[str]) -> Iterator[list[str]]:
"""
Split a shell-style command, as returned by shlex.split, into a sequence
of commands, separated by '&&'.
"""
items = list[str]()
for item in lst:
if item == "&&":
yield items
items = []
else:
items.append(item)
yield items
+154
View File
@@ -0,0 +1,154 @@
import os
import shutil
import ssl
import tarfile
import time
import urllib.request
from collections.abc import Callable
from pathlib import Path, PurePath
from typing import Final
from zipfile import ZipFile
import certifi
from platformdirs import user_cache_path
from cibuildwheel.errors import FatalError
DEFAULT_CIBW_CACHE_PATH: Final[Path] = user_cache_path(appname="cibuildwheel", appauthor="pypa")
CIBW_CACHE_PATH: Final[Path] = Path(
os.environ.get("CIBW_CACHE_PATH", DEFAULT_CIBW_CACHE_PATH)
).resolve()
def ensure_cache_sentinel(cache_path: Path) -> None:
"""Create a sentinel file to mark a cibuildwheel cache directory.
We help prevent accidental deletion of non-cache directories with this.
Args:
cache_path: The cache directory path where the sentinel should be created
"""
if cache_path.exists():
sentinel_file = cache_path / "CACHEDIR.TAG"
if not sentinel_file.exists():
sentinel_file.write_text(
"Signature: 8a477f597d28d172789f06886806bc55\n"
"# This file is a cache directory tag created by cibuildwheel.\n"
"# For information about cache directory tags, see:\n"
"# https://www.brynosaurus.com/cachedir/",
encoding="utf-8",
)
def download(url: str, dest: Path) -> None:
print(f"+ Download {url} to {dest}")
dest_dir = dest.parent
dest_dir.mkdir(parents=True, exist_ok=True)
# we've had issues when relying on the host OS' CA certificates on Windows,
# so we use certifi (this sounds odd but requests also does this by default)
cafile = os.environ.get("SSL_CERT_FILE", certifi.where())
context = ssl.create_default_context(cafile=cafile)
repeat_num = 3
for i in range(repeat_num):
try:
with urllib.request.urlopen(url, context=context) as response:
dest.write_bytes(response.read())
return
except OSError:
if i == repeat_num - 1:
raise
time.sleep(3)
def extract_zip(zip_src: Path, dest: Path) -> None:
"""Extracts a zip and correctly sets permissions on extracted files.
Notes:
- sets permissions to the same values as they were set in the archive
- files with no clear permissions in `external_attr` will be extracted with default values
"""
with ZipFile(zip_src) as zip_:
for zinfo in zip_.filelist:
zip_.extract(zinfo, dest)
# Set permissions to the same values as they were set in the archive
# We have to do this manually due to https://github.com/python/cpython/issues/59999
permissions = (zinfo.external_attr >> 16) & 0o777
if permissions != 0:
dest.joinpath(zinfo.filename).chmod(permissions)
def extract_tar(tar_src: Path, dest: Path) -> None:
"""Extracts a tar file using the stdlib 'tar' filter.
See: https://docs.python.org/3/library/tarfile.html#tarfile.tar_filter for filter details
"""
with tarfile.open(tar_src) as tar_:
tar_.extraction_filter = getattr(tarfile, "tar_filter", (lambda member, _: member))
tar_.extractall(dest)
def move_file(src_file: Path, dst_file: Path) -> Path:
"""Moves a file safely while avoiding potential semantic confusion:
1. `dst_file` must point to the target filename, not a directory
2. `dst_file` will be overwritten if it already exists
3. any missing parent directories will be created
Returns the fully resolved Path of the resulting file.
Raises:
NotADirectoryError: If any part of the intermediate path to `dst_file` is an existing file
IsADirectoryError: If `dst_file` points directly to an existing directory
"""
src_file = src_file.resolve(strict=True)
dst_file = dst_file.resolve()
if dst_file.is_dir():
msg = "dst_file must be a valid target filename, not an existing directory."
raise IsADirectoryError(msg)
dst_file.unlink(missing_ok=True)
dst_file.parent.mkdir(parents=True, exist_ok=True)
# using shutil.move() as Path.rename() is not guaranteed to work across filesystem boundaries
# explicit str() needed for Python 3.8
resulting_file = shutil.move(str(src_file), str(dst_file))
return Path(resulting_file).resolve(strict=True)
def copy_into_local(src: Path, dst: PurePath) -> None:
"""Copy a path from src to dst, regardless of whether it's a file or a directory."""
# Ensure the target folder location exists
Path(dst.parent).mkdir(exist_ok=True, parents=True)
if src.is_dir():
shutil.copytree(src, dst)
else:
shutil.copy(src, dst)
def copy_test_sources(
test_sources: list[str],
project_dir: Path,
test_dir: PurePath,
copy_into: Callable[[Path, PurePath], None] = copy_into_local,
) -> None:
"""Copy the list of test sources from the package to the test directory.
:param test_sources: A list of test paths, relative to the project_dir.
:param project_dir: The root of the project.
:param test_dir: The folder where test sources should be placed.
:param copy_into: The copy function to use. By default, does a local
filesystem copy; but an OCIContainer.copy_info method (or equivalent)
can be provided.
"""
for test_path in test_sources:
source = project_dir.resolve() / test_path
if not source.exists():
msg = f"Test source {test_path} does not exist."
raise FatalError(msg)
copy_into(source, test_dir / test_path)
+174
View File
@@ -0,0 +1,174 @@
import dataclasses
import itertools
import os
import re
import shlex
import textwrap
from collections import defaultdict
from collections.abc import Sequence
from cibuildwheel.typing import PathOrStr
def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str:
"""
Works similarly to `template.format(**kwargs)`, except that unmatched
fields in `template` are passed through untouched.
>>> format_safe('{a} {b}', a='123')
'123 {b}'
>>> format_safe('{a} {b[4]:3f}', a='123')
'123 {b[4]:3f}'
To avoid variable expansion, precede with a single hash e.g.
>>> format_safe('#{a} {b}', a='123')
'{a} {b}'
"""
result = template
for key, value in kwargs.items():
find_pattern = re.compile(
rf"""
(?<!\#) # don't match if preceded by a hash
{{ # literal open curly bracket
{re.escape(key)} # the field name
}} # literal close curly bracket
""",
re.VERBOSE,
)
result = re.sub(
pattern=find_pattern,
repl=str(value).replace("\\", r"\\"),
string=result,
)
# transform escaped sequences into their literal equivalents
result = result.replace(f"#{{{key}}}", f"{{{key}}}")
return result
def prepare_command(command: str, **kwargs: PathOrStr) -> str:
"""
Preprocesses a command by expanding variables like {project}.
For example, used in the test_command option to specify the path to the
project's root. Unmatched syntax will mostly be allowed through.
"""
return format_safe(command, **kwargs)
def strtobool(val: str) -> bool:
return val.lower() in {"y", "yes", "t", "true", "on", "1"}
def unwrap(text: str) -> str:
"""
Unwraps multi-line text to a single line
"""
# remove initial line indent
text = textwrap.dedent(text)
# remove leading/trailing whitespace
text = text.strip()
# remove consecutive whitespace
return re.sub(r"\s+", " ", text)
def unwrap_preserving_paragraphs(text: str) -> str:
"""
Unwraps multi-line text to a single line, but preserves paragraphs
"""
# remove initial line indent
text = textwrap.dedent(text)
# remove leading/trailing whitespace
text = text.strip()
paragraphs = text.split("\n\n")
# remove consecutive whitespace
paragraphs = [re.sub(r"\s+", " ", paragraph) for paragraph in paragraphs]
return "\n\n".join(paragraphs)
def parse_key_value_string(
key_value_string: str,
positional_arg_names: Sequence[str] | None = None,
kw_arg_names: Sequence[str] | None = None,
) -> dict[str, list[str]]:
"""
Parses a string like "docker; create_args: --some-option=value another-option"
"""
if positional_arg_names is None:
positional_arg_names = []
if kw_arg_names is None:
kw_arg_names = []
all_field_names = [*positional_arg_names, *kw_arg_names]
shlexer = shlex.shlex(key_value_string, posix=True, punctuation_chars=";")
shlexer.commenters = ""
shlexer.whitespace_split = True
parts = list(shlexer)
# parts now looks like
# ['docker', ';', 'create_args:', '--some-option=value', 'another-option']
# split by semicolon
fields = [list(group) for k, group in itertools.groupby(parts, lambda x: x == ";") if not k]
result: defaultdict[str, list[str]] = defaultdict(list)
for field_i, field in enumerate(fields):
# check to see if the option name is specified
field_name, sep, first_value = field[0].partition(":")
if sep:
if field_name not in all_field_names:
msg = f"Failed to parse {key_value_string!r}. Unknown field name {field_name!r}"
raise ValueError(msg)
values = ([first_value] if first_value else []) + field[1:]
else:
try:
field_name = positional_arg_names[field_i]
except IndexError:
msg = f"Failed to parse {key_value_string!r}. Too many positional arguments - expected a maximum of {len(positional_arg_names)}"
raise ValueError(msg) from None
values = field
result[field_name] += values
return dict(result)
@dataclasses.dataclass(order=True)
class FlexibleVersion:
version_parts: tuple[int, ...] = dataclasses.field(init=False, repr=False)
suffix: str = dataclasses.field(init=False, repr=False)
version_str: str = dataclasses.field(compare=False)
def __post_init__(self) -> None:
# Split into numeric parts and the optional suffix
match = re.match(r"^[v]?(\d+(\.\d+)*)(.*)$", self.version_str)
if not match:
msg = f"Invalid version string: {self.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)
@staticmethod
def _remove_trailing_zeros(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 __str__(self) -> str:
return self.version_str
+179
View File
@@ -0,0 +1,179 @@
import shlex
from collections.abc import Mapping, Sequence
from dataclasses import dataclass, field
from pathlib import Path, PurePath
from typing import Literal, Self, TypeVar
from packaging.utils import parse_wheel_filename
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call
from cibuildwheel.util.helpers import parse_key_value_string, unwrap
@dataclass(kw_only=True)
class DependencyConstraints:
base_file_path: Path | None = None
packages: list[str] = field(default_factory=list)
def __post_init__(self) -> None:
if self.packages and self.base_file_path is not None:
msg = "Cannot specify both a file and packages in the dependency constraints"
raise ValueError(msg)
if self.base_file_path is not None:
if not self.base_file_path.exists():
msg = f"Dependency constraints file not found: {self.base_file_path}"
raise FileNotFoundError(msg)
self.base_file_path = self.base_file_path.resolve()
@classmethod
def pinned(cls) -> Self:
return cls(base_file_path=resources.CONSTRAINTS)
@classmethod
def latest(cls) -> Self:
return cls()
@classmethod
def from_config_string(cls, config_string: str) -> Self:
if config_string == "pinned":
return cls.pinned()
if config_string == "latest" or not config_string:
return cls.latest()
if config_string.startswith(("file:", "packages:")):
# we only do the table-style parsing if it looks like a table,
# because this option used to be only a file path. We don't want
# to break existing configurations, whose file paths might include
# special characters like ':' or ' ', which would require quoting
# if they were to be passed as a parse_key_value_string positional
# argument.
return cls.from_table_style_config_string(config_string)
return cls(base_file_path=Path(config_string))
@classmethod
def from_table_style_config_string(cls, config_string: str) -> Self:
config_dict = parse_key_value_string(config_string, kw_arg_names=["file", "packages"])
files = config_dict.get("file")
packages = config_dict.get("packages") or []
if files and packages:
msg = "Cannot specify both a file and packages in dependency-versions"
raise ValueError(msg)
if files:
if len(files) > 1:
msg = unwrap("""
Only one file can be specified in dependency-versions.
If you intended to pass only one, perhaps you need to quote the path?
""")
raise ValueError(msg)
return cls(base_file_path=Path(files[0]))
return cls(packages=packages)
def get_for_python_version(
self, *, version: str, variant: Literal["python", "pyodide"] = "python", tmp_dir: Path
) -> Path | None:
if self.packages:
constraint_file = tmp_dir / "constraints.txt"
constraint_file.write_text("\n".join(self.packages))
return constraint_file
if self.base_file_path is not None:
version_parts = version.split(".")
# try to find a version-specific dependency file e.g. if
# ./constraints.txt is the base, look for ./constraints-python36.txt
specific_stem = (
self.base_file_path.stem + f"-{variant}{version_parts[0]}{version_parts[1]}"
)
specific_name = specific_stem + self.base_file_path.suffix
specific_file_path = self.base_file_path.with_name(specific_name)
if specific_file_path.exists():
return specific_file_path
else:
return self.base_file_path
return None
def options_summary(self) -> str | dict[str, str]:
if self == DependencyConstraints.pinned():
return "pinned"
elif self.packages:
return {"packages": " ".join(shlex.quote(p) for p in self.packages)}
elif self.base_file_path is not None:
return self.base_file_path.name
else:
return "latest"
def get_pip_version(env: Mapping[str, str]) -> str:
versions_output_text = call(
"python", "-m", "pip", "freeze", "--all", capture_stdout=True, env=env
)
(pip_version,) = (
version[5:]
for version in versions_output_text.strip().splitlines()
if version.startswith("pip==")
)
return pip_version
T = TypeVar("T", bound=PurePath)
def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
"""
Finds a wheel with an abi3 or a none ABI tag in `wheels` compatible with the Python interpreter
specified by `identifier` that is previously built.
"""
interpreter, platform = identifier.split("-", 1)
interpreter = interpreter.split("_")[0]
free_threaded = interpreter.endswith("t")
if free_threaded:
interpreter = interpreter[:-1]
for wheel in wheels:
_, _, _, tags = parse_wheel_filename(wheel.name)
for tag in tags:
if tag.abi == "abi3" and not free_threaded:
# ABI3 wheels must start with cp3 for impl and tag
if not (interpreter.startswith("cp3") and tag.interpreter.startswith("cp3")):
continue
elif tag.abi == "none":
# CPythonless wheels must include py3 tag
if tag.interpreter[:3] != "py3":
continue
else:
# Other types of wheels are not detected, this is looking for previously built wheels.
continue
if tag.interpreter != "py3" and int(tag.interpreter[3:]) > int(interpreter[3:]):
# If a minor version number is given, it has to be lower than the current one.
continue
if platform.startswith(("manylinux", "musllinux", "macosx", "android", "ios")):
# On these platforms the wheel tag includes a platform version number, which we
# should ignore.
os_, arch = platform.split("_", 1)
if not tag.platform.startswith(os_):
continue
if not tag.platform.endswith(f"_{arch}"):
continue
elif platform.startswith("pyodide"):
# each Pyodide version has its own platform tag
continue
# Windows should exactly match
elif tag.platform != platform:
continue
# If all the filters above pass, then the wheel is a previously built compatible wheel.
return wheel
return None
@@ -0,0 +1,181 @@
import fnmatch
import functools
import json
import platform
import typing
from pathlib import Path
from filelock import FileLock
from cibuildwheel.util.file import download, extract_tar
from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES
class PythonBuildStandaloneAsset(typing.TypedDict):
name: str
url: str
class PythonBuildStandaloneRelease(typing.TypedDict):
tag: str
assets: list[PythonBuildStandaloneAsset]
class PythonBuildStandaloneReleaseData(typing.TypedDict):
releases: list[PythonBuildStandaloneRelease]
@functools.cache
def get_python_build_standalone_release_data() -> PythonBuildStandaloneReleaseData:
with open(PYTHON_BUILD_STANDALONE_RELEASES, "rb") as f:
return typing.cast("PythonBuildStandaloneReleaseData", json.load(f))
class PythonBuildStandaloneError(Exception):
"""Errors related to python-build-standalone."""
def _get_platform_identifiers() -> tuple[str, str, str | None]:
"""
Detects the current platform and returns architecture, platform, and libc
identifiers.
"""
system = platform.system()
machine = platform.machine()
machine_lower = machine.lower()
arch_identifier: str
platform_identifier: str
libc_identifier: str | None = None
# Map Architecture
if machine_lower in ["x86_64", "amd64"]:
arch_identifier = "x86_64"
elif machine_lower in ["aarch64", "arm64"]:
arch_identifier = "aarch64"
else:
msg = f"Unsupported architecture: {system} {machine}. Cannot download appropriate Python build."
raise PythonBuildStandaloneError(msg)
# Map OS + Libc
if system == "Linux":
platform_identifier = "unknown-linux"
libc_identifier = "musl" if "musl" in (platform.libc_ver() or ("", "")) else "gnu"
elif system == "Darwin":
platform_identifier = "apple-darwin"
elif system == "Windows":
platform_identifier = "pc-windows-msvc"
else:
msg = f"Unsupported operating system: {system}. Cannot download appropriate Python build."
raise PythonBuildStandaloneError(msg)
print(
f"Detected platform: arch='{arch_identifier}', platform='{platform_identifier}', libc='{libc_identifier}'"
)
return arch_identifier, platform_identifier, libc_identifier
def _get_pbs_asset(
*,
python_version: str,
arch_identifier: str,
platform_identifier: str,
libc_identifier: str | None,
) -> tuple[str, str, str]:
"""Finds the asset, returning (tag, filename, url)."""
release_data = get_python_build_standalone_release_data()
expected_suffix = f"{arch_identifier}-{platform_identifier}"
if libc_identifier:
expected_suffix += f"-{libc_identifier}"
expected_suffix += "-install_only.tar.gz"
asset_pattern = f"cpython-{python_version}.*-{expected_suffix}"
print(f"Looking for file with pattern {asset_pattern}")
for release in release_data["releases"]:
for asset in release["assets"]:
asset_name = asset["name"]
if not fnmatch.fnmatch(asset_name, asset_pattern):
continue
asset_url = asset["url"]
return release["tag"], asset_url, asset_name
# If loop completes without finding a match
msg = f"Could not find python-build-standalone release asset matching {asset_pattern!r}."
raise PythonBuildStandaloneError(msg)
def _download_or_get_from_cache(asset_url: str, asset_filename: str, cache_dir: Path) -> Path:
with FileLock(cache_dir / (asset_filename + ".lock")):
asset_cache_path = cache_dir / asset_filename
if asset_cache_path.is_file():
print(f"Using cached python_build_standalone: {asset_cache_path}")
return asset_cache_path
print(f"Downloading python_build_standalone: {asset_url} to {asset_cache_path}")
download(asset_url, asset_cache_path)
return asset_cache_path
def _find_python_executable(extracted_dir: Path) -> Path:
"""Finds the python executable within the extracted directory structure."""
# Structure is typically 'python/bin/python' or 'python/python.exe'
base_install_dir = extracted_dir / "python"
if platform.system() == "Windows":
executable_path = base_install_dir / "python.exe"
else:
executable_path = base_install_dir / "bin" / "python"
if not executable_path.is_file():
msg = f"Could not locate python executable at expected path {executable_path} within {extracted_dir}."
raise PythonBuildStandaloneError(msg)
print(f"Found python executable: {executable_path}")
return executable_path.resolve() # Return absolute path
def create_python_build_standalone_environment(
python_version: str, temp_dir: Path, cache_dir: Path
) -> Path:
"""
Returns a Python environment from python-build-standalone, downloading it
if necessary using a cache, and expanding it into a fresh base path.
Args:
python_version: The Python version string (e.g., "3.12").
temp_dir: A directory where the Python environment will be created.
cache_dir: A directory to store/retrieve downloaded archives.
Returns:
The absolute path to the python executable within the created environment (in temp_dir).
Raises:
PythonBuildStandaloneError: If the platform is unsupported, the build cannot be found,
download/extraction fails, or configuration is invalid.
"""
print(f"Creating python-build-standalone environment: version={python_version}")
arch_id, platform_id, libc_id = _get_platform_identifiers()
pbs_tag, asset_url, asset_filename = _get_pbs_asset(
python_version=python_version,
arch_identifier=arch_id,
platform_identifier=platform_id,
libc_identifier=libc_id,
)
print(f"Using python-build-standalone release: {pbs_tag}")
archive_path = _download_or_get_from_cache(
asset_url=asset_url, asset_filename=asset_filename, cache_dir=cache_dir
)
python_base_dir = temp_dir / "pbs"
assert not python_base_dir.exists()
extract_tar(archive_path, python_base_dir)
return _find_python_executable(python_base_dir)
+32
View File
@@ -0,0 +1,32 @@
import functools
import tomllib
from pathlib import Path
from typing import Final
from cibuildwheel.typing import PlatformName
PATH: Final[Path] = Path(__file__).parent.parent / "resources"
INSTALL_CERTIFI_SCRIPT: Final[Path] = PATH / "install_certifi.py"
FREE_THREAD_ENABLE_313: Final[Path] = PATH / "free-threaded-enable-313.xml"
FREE_THREAD_ENABLE_314: Final[Path] = PATH / "free-threaded-enable-314.xml"
NODEJS: Final[Path] = PATH / "nodejs.toml"
DEFAULTS: Final[Path] = PATH / "defaults.toml"
PINNED_DOCKER_IMAGES: Final[Path] = PATH / "pinned_docker_images.cfg"
BUILD_PLATFORMS: Final[Path] = PATH / "build-platforms.toml"
CONSTRAINTS: Final[Path] = PATH / "constraints.txt"
VIRTUALENV: Final[Path] = PATH / "virtualenv.toml"
CIBUILDWHEEL_SCHEMA: Final[Path] = PATH / "cibuildwheel.schema.json"
PYTHON_BUILD_STANDALONE_RELEASES: Final[Path] = PATH / "python-build-standalone-releases.json"
TEST_FAIL_CWD_FILE: Final[Path] = PATH / "testing_temp_dir_file.py"
# this value is cached because it's used a lot in unit tests
@functools.cache
def read_all_configs() -> dict[str, list[dict[str, str]]]:
with BUILD_PLATFORMS.open("rb") as f:
loaded_file = tomllib.load(f)
return {k: list[dict[str, str]](v["python_configurations"]) for k, v in loaded_file.items()}
def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
return read_all_configs()[config]
+169
View File
@@ -0,0 +1,169 @@
import contextlib
import functools
import os
import shutil
import sys
import tomllib
from collections.abc import Sequence
from pathlib import Path
from typing import Final
from filelock import FileLock
from packaging.requirements import InvalidRequirement, Requirement
from packaging.version import Version
from cibuildwheel.util import resources
from cibuildwheel.util.cmd import call
from cibuildwheel.util.file import CIBW_CACHE_PATH, download
_IS_WIN: Final[bool] = sys.platform.startswith("win")
@functools.cache
def _ensure_virtualenv(version: str) -> tuple[Path, Version]:
version_parts = version.split(".")
key = f"py{version_parts[0]}{version_parts[1]}"
with resources.VIRTUALENV.open("rb") as f:
loaded_file = tomllib.load(f)
configuration = loaded_file.get(key, loaded_file["default"])
version = str(configuration["version"])
url = str(configuration["url"])
path = CIBW_CACHE_PATH / f"virtualenv-{version}.pyz"
with FileLock(str(path) + ".lock"):
if not path.exists():
download(url, path)
return (path, Version(version))
def constraint_flags(
dependency_constraint: Path | None,
) -> Sequence[str]:
"""
Returns the flags to pass to pip for the given dependency constraint.
"""
return ["-c", dependency_constraint.as_uri()] if dependency_constraint else []
def _parse_pip_constraint_for_virtualenv(
constraint_path: Path | None,
) -> str:
"""
Parses the constraints file referenced by `dependency_constraint_flags` and returns a dict where
the key is the package name, and the value is the constraint version.
If a package version cannot be found, its value is "embed" meaning that virtualenv will install
its bundled version, already available locally.
The function does not try to be too smart and just handles basic constraints.
If it can't get an exact version, the real constraint will be handled by the
{macos|windows}.setup_python function.
"""
if constraint_path:
assert constraint_path.exists()
with constraint_path.open(encoding="utf-8") as constraint_file:
for line_ in constraint_file:
line = line_.strip()
if not line:
continue
if line.startswith("#"):
continue
try:
requirement = Requirement(line)
package = requirement.name
if (
package != "pip"
or requirement.url is not None
or requirement.marker is not None
or len(requirement.extras) != 0
or len(requirement.specifier) != 1
):
continue
specifier = next(iter(requirement.specifier))
if specifier.operator != "==":
continue
return specifier.version
except InvalidRequirement:
continue
return "embed"
def virtualenv(
version: str,
python: Path,
venv_path: Path,
dependency_constraint: Path | None,
*,
use_uv: bool,
env: dict[str, str] | None = None,
pip_version: str | None = None,
) -> dict[str, str]:
"""
Create a virtual environment. If `use_uv` is True,
dependency_constraint_flags are ignored since nothing is installed in the
venv. Otherwise, pip is installed.
"""
# virtualenv may fail if this is a symlink.
python = python.resolve()
assert python.exists()
if use_uv:
uv_path = find_uv()
assert uv_path is not None
call(uv_path, "venv", venv_path, "--python", python)
else:
virtualenv_app, virtualenv_version = _ensure_virtualenv(version)
if pip_version is None:
pip_version = _parse_pip_constraint_for_virtualenv(dependency_constraint)
additional_flags = [f"--pip={pip_version}", "--no-setuptools"]
if virtualenv_version < Version("20.31") or Version(version) < Version("3.9"):
additional_flags.append("--no-wheel")
# Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
# environment. The initial cost is a bit higher but reusing is much faster.
# Windows does not always allow symlinks so just disabling for now.
# Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
# version of pip that will end-up installed.
# c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
if not _IS_WIN and pip_version != "embed" and Version(pip_version) >= Version("19.3"):
additional_flags.append("--symlink-app-data")
call(
sys.executable,
"-sS", # just the stdlib, https://github.com/pypa/virtualenv/issues/2133#issuecomment-1003710125
virtualenv_app,
"--activators=",
"--no-periodic-update",
*additional_flags,
"--python",
python,
venv_path,
)
paths = [str(venv_path), str(venv_path / "Scripts")] if _IS_WIN else [str(venv_path / "bin")]
venv_env = os.environ.copy() if env is None else env.copy()
venv_env["PATH"] = os.pathsep.join([*paths, venv_env["PATH"]])
venv_env["VIRTUAL_ENV"] = str(venv_path)
if not use_uv and pip_version == "embed":
call(
"python",
"-m",
"pip",
"install",
"--upgrade",
"pip",
*constraint_flags(dependency_constraint),
env=venv_env,
cwd=venv_path,
)
return venv_env
def find_uv() -> Path | None:
# Prefer uv in our environment
with contextlib.suppress(ImportError, FileNotFoundError):
from uv import find_uv_bin # noqa: PLC0415
return Path(find_uv_bin())
uv_on_path = shutil.which("uv")
return Path(uv_on_path) if uv_on_path else None

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