Skip to content

feat(python)!: abi3t wheels for CPython 3.15, require CPython 3.12 - #162

Merged
brndnmtthws merged 3 commits into
mainfrom
python-3.15-abi3t
Oct 14, 2026
Merged

brndnmtthws merged 3 commits into
mainfrom
python-3.15-abi3t

Conversation

@brndnmtthws

@brndnmtthws brndnmtthws commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

hessboost now ships a PEP 803 abi3t wheel for CPython 3.15 and newer. It is a single extension that both the GIL-enabled and the free-threaded builds load, and uv and pip pick it over the abi3 wheel there. The minimum CPython goes from 3.11 to 3.12, and CI now tests every wheel family on every platform.

Wheel Installed on ci.yml tests publish.yml tests
cp312-abi3 GIL-enabled 3.12–3.14 3.12, 3.14 on 4 OSes 3.12, 3.14 on 6 platforms; CUDA runtime on 3.14
cp315-abi3.abi3t GIL-enabled and free-threaded 3.15+ 3.15, 3.15t on 4 OSes 3.15, 3.15t on 6 platforms; CUDA runtime on both
cp314-cp314t free-threaded 3.14 3.14t on x86_64 Linux 3.14t on 6 platforms; CUDA runtime

publish.yml now expects 18 hessboost wheels (was 12) and 6 hessboost-runtime-cuda wheels (was 4).

User Impact

  • CPython 3.15: supported, GIL-enabled and free-threaded, from one wheel per platform. Later CPython releases load the same wheel, so no new wheel is needed per release.
  • CPython 3.11: no longer supported. Installers on 3.11 keep resolving 0.3.1 through requires-python. This is breaking for Python users, so the next release should be a minor bump.
  • CPython 3.14t: keeps its own version-specific wheel. CPython 3.14 has no stable ABI for free-threaded builds, and PyO3 recommends exactly this set: abi3 + cp314t + abi3t.
  • numpy on 3.15+: requires numpy >= 2.5 there, because the abi3t extension reads arrays through NumPy 2.5's C API. Older numpy would otherwise fail at the first array call. numpy 2.5.2+ is also the first numpy with any cp315 wheels.
  • Public type aliases (ModelFormat, PathLike, Column, Method, ...) are now PEP 695 type statements. At runtime they are TypeAliasType objects instead of the unions they name. Nothing in the package introspects them.

Decisions

  • Minimum CPython 3.12. PyPI downloads over the last 30 days (pypistats):

    Package 3.11 3.12 3.13 3.14 3.15
    numpy 18.6% 31.4% 15.8% 6.1% 0.1%
    scikit-learn 15.0% 27.0% 11.8% 4.8% 0.0%
    LightGBM 15.9% 29.5% 12.1% 5.1% 0.0%
    XGBoost 17.2% 18.0% 13.4% 4.4% 0.0%

    3.12 is the most-used version, so it stays. numpy 2.5 and SciPy 1.18 already require 3.12, and SPEC 0 dropped 3.11 in Q4 2025.

  • rust-numpy from a pinned git revision (aca95e9, current main). Its abi3t support (add support for abi3t PyO3/rust-numpy#556) is not released yet: 0.29.0 is from June. Switch back to crates.io once a release carries it; update pyo3 to 0.30 PyO3/rust-numpy#572 (PyO3 0.30) is the likely one. PyO3 stays at 0.29.3. The sdist build fetches the revision through Cargo, and the sdist round trip exercises that.

  • PyO3 features abi3-py312 and abi3t-py315. The build interpreter selects the extension: 3.12–3.14 build abi3, 3.15+ (either build) build abi3t, and 3.14t builds version-specific. Every build therefore passes -i explicitly.

  • Explicit, GIL-pinned Python versions in CI. For 3.14 and newer, uv takes an installed free-threaded interpreter for a plain version request, and keeps an existing free-threaded environment for one. With only 3.15t installed, uv python find 3.15 returns 3.15t, and uv sync --python 3.14 keeps a 3.14t .venv. The first CI run hit this twice: publish.yml tested the abi3 wheels on 3.14t, which refuses them, and ci.yml's "3.15" runs ran on the 3.15t build interpreter. test-wheel.sh now runs uv python install <version> (a plain version downloads the GIL-enabled build) and then requests <version>+gil unless the version ends in t. The old "run 3 first" ordering is gone: 3 would take any installed CPython.

  • uv 0.13.1 in mise: the first uv with CPython 3.15.0 final (0.12.23 stopped at rc3). The Alpine test image moves to uv:0.13-alpine.

Validation

All local runs were on aarch64 Linux.

  • Wheel builds: maturin 1.15.0 built cp312-abi3, cp315-abi3.abi3t, and cp314-cp314t.
  • Wheel tests (test-wheel.sh per interpreter):
    • abi3 on 3.12 and 3.14: 435 passed each.
    • abi3t on 3.15: 435 passed.
    • abi3t on 3.15t: 424 passed (polars is left out there). The GIL stayed disabled after import.
    • cp314t on 3.14t: 424 passed.
    • Interpreter switching: running 3.14t → 3.14 → 3.15t → 3.15 → 3.12 in a row through one python/.venv gives each run the requested build.
  • Installer selection: uv 0.13.1 and pip 26.2.1 pick _native.abi3.so on 3.12 and 3.14, cpython-314t on 3.14t, and _native.abi3t.so on 3.15 and 3.15t.
  • sdist: maturin sdist, then an abi3t wheel built from the sdist on 3.15t.
  • Python crate lints: cargo clippy --all-targets -D warnings passes under all three ABI configurations, with and without --features cuda. cargo fmt --check passes.
  • Python lints: ruff check, ruff format --check, and ty check are clean on the 3.12 target. pyright --verifytypes reports 100% completeness and 0 missing docstrings.
  • Scripts and workflows: shellcheck and actionlint (with its shellcheck integration) are clean.
  • Left to CI: macOS and Windows wheels (including python3t.dll on GIL-enabled Windows 3.15), musllinux in Alpine, the manylinux images' /opt/python/cp315-cp315t, and the CUDA runtime wheels.

uv 0.12.23 knows CPython 3.15 only up to 3.15.0rc3; 0.13.0 adds 3.15.0
final (and makes it the default stable version).
Three wheels per platform, each built with its family's oldest CPython:
cp312-abi3 (GIL-enabled 3.12 to 3.14), cp315-abi3.abi3t (PEP 803: both
builds of 3.15 and newer, which uv and pip prefer there), and
cp314-cp314t (free-threaded 3.14 has no stable ABI). PyO3's abi3-py312
and abi3t-py315 features let the build interpreter pick the extension.

- rust-numpy comes from a pinned git revision: its abi3t support
  (PyO3/rust-numpy#556) is not in a release yet. The abi3t extension
  reads arrays through NumPy 2.5's C API, so CPython 3.15 and newer
  require numpy>=2.5.
- requires-python >=3.12: numpy 2.5 and SciPy 1.18 already dropped 3.11,
  and 3.12 is the most-used CPython. ruff and ty target 3.12, and the
  type aliases become PEP 695 `type` statements.
- CI builds the abi3 and abi3t wheels on every platform (cp314t on
  x86_64 Linux) and tests them on 3.12 and 3.14, 3.15 and 3.15t (3.14t);
  publish.yml builds and tests all three, and the CUDA runtime's, on every
  platform. The tests name each version, since uv's "3" also matches an
  installed free-threaded interpreter.

BREAKING CHANGE: CPython 3.11 is no longer supported; installers there
keep resolving hessboost 0.3.1.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T14:27:21.820346Z 6d2c675 New commits
🔒 Security Review ✅ Completed 2026-10-14T14:26:48.584594Z 6d2c675 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

For CPython 3.14 and newer uv takes an installed free-threaded
interpreter for a plain version request, and keeps a free-threaded
project environment for one. publish.yml therefore tested the abi3
wheels against 3.14t, which refuses them. ci.yml tested its "3.15" runs
on the 3.15t build interpreter. test-wheel.sh now installs the requested
version (uv downloads the GIL-enabled build for a plain version) and
asks for it as "+gil".
@brndnmtthws
brndnmtthws merged commit 023c110 into main Oct 14, 2026
38 checks passed
@brndnmtthws
brndnmtthws deleted the python-3.15-abi3t branch October 14, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant