Skip to content

Add Python 3.13 and 3.14 wheel builds#809

Closed
cidrblock wants to merge 1 commit intoansible:develfrom
cidrblock:mo_python
Closed

Add Python 3.13 and 3.14 wheel builds#809
cidrblock wants to merge 1 commit intoansible:develfrom
cidrblock:mo_python

Conversation

@cidrblock
Copy link
Contributor

@cidrblock cidrblock commented Mar 9, 2026

SUMMARY

Add Python 3.13 and 3.14 wheel builds to unblock downstream consumers
(e.g. ansible.netcommon via ansible-pylibssh).

Per-file change map:

File Change Why
setup.cfg Add 3.13/3.14 classifiers Direct: declare supported versions
packaging/pep517_backend/_backend.py Cython version bounds per Python Direct: Cython >= 3.1.0 for 3.14, >= 3.0.11 for 3.13 (3.0.x has known build failures on 3.14)
.github/workflows/reusable-cibuildwheel.yml cibuildwheel v2.17.0 → v3.3.1 Direct: v3 is the minimum version that supports cp314 wheel building
.github/workflows/ci-cd.yml Add 3.13/3.14 to test/build matrices Direct: test and build wheels for new versions
.github/workflows/ci-cd.yml Add *t-* to CIBW_SKIP in 3 jobs Cascade (cibuildwheel v3): free-threaded builds are no longer experimental and built by default; Cython extensions don't set Py_mod_gil_not_used
.github/workflows/ci-cd.yml sdist name uses f"sdist={sdist_file_prj_base_name}" Cascade (setuptools): PEP 625 normalization changed the sdist filename from hyphens to underscores
.github/workflows/ci-cd.yml RPM source copy uses env: block Cascade (setuptools): rename PEP 625-normalized sdist to match RPM spec's Source0; also moves GHA expressions out of run: to avoid script injection
pyproject.toml Skip *t-*, armv7l, riscv64; set MACOSX_DEPLOYMENT_TARGET Cascade (cibuildwheel v3): *t-* — see above; armv7l/riscv64CIBW_ARCHS_LINUX=all in the odd-arches job now attempts these since v3 has working support, but no libssh container images exist; MACOSX_DEPLOYMENT_TARGET=15.0delocate 0.13.0 now errors on deployment-target mismatch; brew-installed openssl@3 (3.6.1) on macos-15-intel has a minimum target of 15.0 (CI evidence)
.coveragerc Add core = ctrace Cascade (Python 3.14): Cython coverage plugin doesn't support 3.14's sys.monitoring tracer; ctrace avoids the issue (cf. coveragepy#2099)
requirements-build.txt Re-compiled lockfile Cascade (Cython bump): updating Cython from 3.0.12 to 3.1.8 required re-running pip-compile; this incidentally resolved setuptools from 68.2.2 to 82.0.1, which introduced PEP 625 sdist normalization
.packit.yml Hardcode ansible_pylibssh-{version} Cascade (setuptools): {upstream_pkg_name} resolves to hyphens but the sdist directory is now underscores per PEP 625
packaging/rpm/ansible-pylibssh.spec Add %{sdist_name} macro, update %autosetup Cascade (setuptools): extracted source directory now uses underscores per PEP 625
docs/changelog-fragments/809.packaging.rst Changelog fragment Direct
docs/changelog-fragments/809.contrib.rst Changelog fragment Direct

Note on setuptools / PEP 625 scope:
Re-running pip-compile for the Cython update incidentally resolved setuptools from 68.2.2 to 82.0.1 (no dependency forces this — setuptools-scm only requires >= 61). The newer setuptools introduced PEP 625 sdist filename normalization (underscores), which cascaded to .packit.yml, the RPM spec, and CI workflow changes. Constraining setuptools to the old version would avoid these changes but deliberately hold back standard-compliant behavior. If preferred, these can be split into a separate preparatory PR.

Fixes #781

ISSUE TYPE
  • Feature Pull Request
ADDITIONAL INFORMATION

Motivation:
Collections depending on ansible-pylibssh (e.g. via ansible.netcommon) fail to install on Python 3.13+ because no pre-built wheels are available, and building from source requires libssh development headers that are typically not installed.

Notes:

  • Custom manylinux images rebuild weekly from latest PyPA base images, which already include Python 3.13/3.14 interpreters. No Dockerfile changes needed.
  • Free-threaded builds (cp314t) are skipped per cibuildwheel v3.1.0 guidance: "you have to skip it explicitly with 'cp31?t-*' if you don't support it yet."
  • macOS wheels require macOS 15.0+ (Sequoia) — delocate 0.13.0 enforces deployment-target consistency and the brew-installed openssl@3 (3.6.1) bottle on macos-15-intel has a minimum target of 15.0 (CI error log).
  • armv7l/riscv64 skip entries are defensive: CIBW_ARCHS_LINUX=all was already set in the odd-arches job; cibuildwheel v3 has working support for these arches, so builds now fail loudly rather than silently. No container images with libssh exist for them.

Test plan:

  • CI builds cp313 and cp314 wheels successfully on Linux (manylinux)
  • CI builds cp313 and cp314 wheels successfully on macOS
  • Smoke tests pass for 3.13 and 3.14 on all platforms
  • Existing 3.9–3.12 wheels still build and pass tests (no regression)
  • sdist builds succeed across all Python versions in the matrix
  • RPM builds succeed on Fedora and UBI containers
  • Packit SRPM, rpm-build, and testing-farm checks succeed
  • manylinux [odd] QEMU wheel builds (aarch64, ppc64le, s390x)

Copilot AI review requested due to automatic review settings March 9, 2026 14:42
cidrblock added a commit to cidrblock/pylibssh that referenced this pull request Mar 9, 2026
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided There is a change note present in this PR label Mar 9, 2026
@packit-as-a-service
Copy link

Congratulations! One of the builds has completed. 🍾

You can install the built RPMs by following these steps:

  • sudo dnf install -y 'dnf*-command(copr)'
  • dnf copr enable packit/ansible-pylibssh-809
  • And now you can install the packages.

Please note that the RPMs should be used only in a testing environment.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates packaging metadata and CI/build tooling to produce and test wheels for newer CPython versions (3.13 and 3.14), reducing installation friction for downstream users who otherwise need system libssh headers to build from source.

Changes:

  • Adds Python 3.13/3.14 trove classifiers in setup.cfg.
  • Updates build constraints and PEP 517 backend build requirements to use newer Cython for Python 3.14.
  • Expands CI matrices to include 3.13/3.14 and bumps cibuildwheel GitHub Action to a version that supports these interpreters.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
setup.cfg Adds Python 3.13/3.14 classifiers.
requirements-build.txt Updates the pinned Cython build constraint version.
packaging/pep517_backend/_backend.py Adjusts conditional Cython build requirements for 3.13 vs 3.14+.
.github/workflows/reusable-cibuildwheel.yml Bumps pypa/cibuildwheel action to a newer major version.
.github/workflows/ci-cd.yml Extends build/test matrices to include Python 3.13 and 3.14.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Member

@webknjaz webknjaz left a comment

Choose a reason for hiding this comment

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

FYI, a part of this is being worked on in #808, the other part requires preparatory work like #795, #744 and some others. Once you're done experimenting here, make sure to send atomic PRs for every individual change being made. I'm definitely not going to accept a bunch of unrelated changes smashed together .

@cidrblock
Copy link
Contributor Author

cidrblock commented Mar 9, 2026

FYI, a part of this is being worked on in #808, the other part requires preparatory work like #795, #744 and some others. Once you're done experimenting here, make sure to send atomic PRs for every individual change being made. I'm definitely not going to accept a bunch of unrelated changes smashed together .

The PR summary has been updated to map changes to the addition of python3.13/3.14. There are no unrelated changes in this PR.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

.github/workflows/ci-cd.yml:330

  • The f-strings here use a space before the conversion flag ({sdist_file_prj_base_name !s} / {whl_file_prj_base_name !s}), which is invalid Python syntax and will make this workflow step fail at runtime. Remove the whitespace (or drop the !s conversion entirely) so the print(...) calls are syntactically valid.
                f"sdist={sdist_file_prj_base_name !s}-${{
                    steps.request-check.outputs.release-requested == 'true'
                    && github.event.inputs.release-version
                    || steps.scm-version.outputs.dist-version
                }}.tar.gz",
                file=outputs_file,
            )
            print(
                f"wheel={whl_file_prj_base_name !s}-${{
                    steps.request-check.outputs.release-requested == 'true'
                    && github.event.inputs.release-version
                    || steps.scm-version.outputs.dist-version
                }}-cp3*-cp3*-*.whl",
                file=outputs_file,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@cidrblock cidrblock force-pushed the mo_python branch 2 times, most recently from 9b52ef2 to 37902e3 Compare March 9, 2026 20:00
@webknjaz
Copy link
Member

webknjaz commented Mar 9, 2026

How about following the PR template, though?

@cidrblock
Copy link
Contributor Author

Updated the PR description to follow the template structure (SUMMARY / ISSUE TYPE / ADDITIONAL INFORMATION). The detailed breakdown of the causal chain is retained under ADDITIONAL INFORMATION since the cascading dependencies between changes warrant the context.

Also pushed code changes addressing the other review comments:

  • Replaced the pytest.ini warning filter with core = ctrace in .coveragerc (per the aiohttp approach)
  • Added inline comments documenting the *t-* skip rationale in all three CIBW_SKIP blocks and in pyproject.toml
  • Updated 809.packaging.rst with RST cross-reference roles and created a separate 809.contrib.rst for CI/infra changes

@cidrblock
Copy link
Contributor Author

cidrblock commented Mar 9, 2026

@webknjaz — Updated the PR description with a per-file change map showing each file's change and its justification (direct vs cascade). Also added an honest note about the setuptools/PEP 625 scope: the bump from 68.2.2 to 82.0.1 was incidental from re-running pip-compile for the Cython update (no dependency forces it — setuptools-scm only requires >= 61). Happy to split the PEP 625 normalization changes into a separate preparatory PR if that's preferred.

Other updates since last review:

  • MACOSX_DEPLOYMENT_TARGET lowered from 15.0 to 14.0 (brew Intel bottles target Sonoma, not Sequoia)
  • core = ctrace in .coveragerc replaces the pytest.ini warning filter (per your suggestion to follow the aiohttp approach)
  • Inline comments added for *t-* skip rationale
  • Changelog fragments updated with RST roles and split into 809.packaging.rst + 809.contrib.rst
  • Commits squashed to a single commit

@cidrblock cidrblock force-pushed the mo_python branch 2 times, most recently from c837497 to 820ec40 Compare March 10, 2026 03:31
Bump cibuildwheel from v2.17.0 to v3.3.1 and Cython from 3.0.12
to 3.1.8 to enable cp313/cp314 wheel building. Add Python 3.13
and 3.14 to CI test and build matrices.

Cascading changes from the cibuildwheel v2 to v3 upgrade:
- Re-compile requirements-build.txt (setuptools 68.2.2 to 82.0.1)
- Adapt CI workflow and Packit config for PEP 625 sdist normalization
- Skip free-threaded (*t-*) builds (Cython lacks Py_mod_gil_not_used)
- Skip armv7l/riscv64 (no container images for these arches)
- Set MACOSX_DEPLOYMENT_TARGET=14.0 (delocate 0.13.0 enforces
  deployment-target consistency with brew-installed dylibs)
- Configure core=ctrace in .coveragerc for Python 3.14 compatibility

Made-with: Cursor
'pr': (f'{github_repo_url}/pull/%s', 'PR #%s'),
'commit': (f'{github_repo_url}/commit/%s', '%s'),
'gh': (f'{github_url}/%s', 'GitHub: %s'),
'pypi': ('https://pypi.org/project/%s', '%s'),
Copy link
Member

Choose a reason for hiding this comment

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

🤔 I didn't realize sphinx-issues isn't yet integrated here. Will have to add it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The :pypi: extlink was added here to satisfy the review request for RST roles in the changelog fragments. sphinx-issues would be a cleaner long-term solution.

Copy link
Member

Choose a reason for hiding this comment

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

xref #818

@webknjaz
Copy link
Member

Looks like there's no atomic commits even. This will make handling the patch more difficult.

webknjaz added a commit to webknjaz/ansible--pylibssh that referenced this pull request Mar 10, 2026
A variation of a portion of this patch was also independently
demonstrated in PR ansible#809. This is dictated by PEP 625 which modern
versions of `setuptools` implement.

Co-Authored-By: Bradley A. Thornton <bthornto@redhat.com>
@webknjaz webknjaz self-assigned this Mar 10, 2026
@webknjaz webknjaz linked an issue Mar 11, 2026 that may be closed by this pull request
@cidrblock
Copy link
Contributor Author

All review comments have been addressed or acknowledged. The maintainer has indicated he will arrive at Python 3.13/3.14 support through his own sequencing, with portions of this work already absorbed into #808. Closing this PR to avoid further conflict with that approach.

The technical findings from this PR remain available as reference:

  • cibuildwheel v3.3.1 is required for cp314 wheel building
  • *t-* free-threaded builds must be skipped until Cython sets Py_mod_gil_not_used
  • MACOSX_DEPLOYMENT_TARGET=15.0 is required due to delocate 0.13.0 enforcement against brew-installed openssl@3
  • core = ctrace in .coveragerc resolves Python 3.14 coverage compatibility
  • Cython >= 3.1.0 is required for Python 3.14; >= 3.0.11 for Python 3.13

Thank you for the detailed review.

@cidrblock cidrblock closed this Mar 11, 2026
@webknjaz webknjaz moved this from 🧐 @webknjaz's review queue 📋 to 🚧 In progress 🚧 in 📅 Procrastinating in public 😵‍💫 Mar 13, 2026
test-command = "python -Im pytest -m smoke --no-cov {project}/tests"
skip = "pp*"
skip = [
"pp*", # FIXME: we don't ship these currently but could
Copy link
Member

Choose a reason for hiding this comment

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

In #821, I discovered that having this triggers a warning in cibuildwheel v3+ which has a flipped default for PyPy and has been overlooked in this patch.

webknjaz added a commit to webknjaz/ansible--pylibssh that referenced this pull request Mar 14, 2026
These do not currently have a suitable infra and will have to be
handled separately. `cibuildwheel` v3.1.2 enables this architecture by
default so this patch configures the build system to skip the arch.

Brad first discovered this in PR ansible#809.

Co-Authored-By: Bradley A. Thornton <bthornto@redhat.com>
Comment on lines +769 to 774
- python-version: "3.13"
runner-vm-os: ubuntu-22.04
store-sdist-to-artifact: false
- python-version: "3.12"
runner-vm-os: ubuntu-22.04
store-sdist-to-artifact: true
Copy link
Member

Choose a reason for hiding this comment

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

FTR, this should've gone into the main matrix as 2 lines as they are smoke tests and all of the factors are already set up there and don't need repeating. Only the highest version (3.14) would be configured differently to save the artifact for further use in the following CI jobs.

Copy link
Member

Choose a reason for hiding this comment

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

Even better — it'd be enough to just update 3.12 to 3.14 here as the oldest supported Python versions are the smoke test and there's really no need to test everything in between sdist-wise. This was added mainly as a regression guardrails, IIRC, and was just a single job at some point until something broke.

"""
c_ext_build_deps = [
'Cython >= 3.0.11; python_version >= "3.13"', # Ideally >= 3.1.0
'Cython >= 3.1.0; python_version >= "3.14" and python_version < "3.15"',
Copy link
Member

Choose a reason for hiding this comment

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

Leaving the crusial dep missing under Python 3.15 is a practice highly discouraged by PyPA. There must be no upper bound in such cases — it's quite a cruel thing to do. Otherwise, it's a straight way towards backtracking and/or breaking future runtime users ruthlessly.


Bumped :pypi:`cibuildwheel` from v2.17.0 to v3.3.1 (required for
``cp313``/``cp314`` wheel building) and :pypi:`Cython` from 3.0.12
to 3.1.8 (Cython 3.0.x has known build failures with Python 3.14).
Copy link
Member

Choose a reason for hiding this comment

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

No proof of this. I confirmed that Cython 3.1 is needed for free-threading support. With FT disabled, there's no justification for setting a bar higher than it needs to be.

@webknjaz webknjaz moved this from 🚧 In progress 🚧 to 🤦‍♂️ LLM/“AI” slop 🤖 in 📅 Procrastinating in public 😵‍💫 Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TODO] Fix coverage collection for Cython modules No binary wheel for py313 published on pypi

3 participants