Skip to content

Add Python 3.15 support - #357

Open
zydo wants to merge 6 commits into
indygreg:mainfrom
zydo:python-3.15
Open

zydo wants to merge 6 commits into
indygreg:mainfrom
zydo:python-3.15

Conversation

@zydo

@zydo zydo commented Oct 12, 2026 •

Copy link
Copy Markdown

Adds Python 3.15 support:

  • Build cp315 and cp315t wheels on Linux, macOS and Windows. Like 3.14, Linux wheels use manylinux_2_28 and musllinux_1_2 only.
  • Add 3.15 and 3.15t to the test and typing CI matrices.
  • Add the 3.15 trove classifier and list 3.15 as supported in the docs.

No C, CFFI or Python code changes were needed. On every 3.15 and 3.15t test job, the C and CFFI backends pass in full, and the Rust backend (with the PyO3 0.29 upgrade) has only the test_optimize failure it also has on main.

Refs #354. This makes CI build the 3.15 wheels; publishing them to PyPI still needs a release.

Depends on #356 (PyO3 0.29, which depends on #358); only the last commit belongs to this PR.

All workflows pass on the fork: https://github.com/zydo/python-zstandard/actions?query=branch%3Apython-3.15

zydo added 6 commits October 12, 2026 03:06
load_setup_py_data() executes setup.py in the conda-build process.
setup.py prepends "." to sys.path, so the source tree's unbuilt
zstandard package later shadows the zstandard bindings that
conda-package-handling imports for .conda support. conda-build then
fails with "Didn't recognize extension for file '...conda'".

Read the version from c-ext/python-zstandard.h with load_file_regex()
instead.
cibuildwheel installs the test-groups packages unlocked, so it picked up
the latest mypy, which now depends on ast-serialize. ast-serialize has no
wheels for musllinux_1_1 or musllinux s390x, and building it from source
needs a Rust toolchain the containers don't have, failing those wheel
jobs before any test ran.

Move the test suite's dependencies into a new "test" dependency group,
have "dev" include it plus mypy, and point cibuildwheel at "test".
On Apple Silicon runners Homebrew installs into /opt/homebrew, which
isn't on the compiler's default search paths, so the --system-zstd build
failed with "'zdict.h' file not found". Pass Homebrew's include and lib
directories explicitly, and build for arm64 only since Homebrew's libzstd
is not universal2.
test_stream_source_readinto intermittently fails on macos-26-intel
runners with DeadlineExceeded: a single example took 31s against the 20s
deadline, then 18s on hypothesis's rerun, which hypothesis reports as a
FlakyFailure. The timing depends on runner load, not on the code under
test, so don't enforce a deadline in the ci profile, as the expensive
profile already does.
PyO3 0.29 is the first release that supports Python 3.15.

Port the Rust backend to the APIs renamed or removed since 0.25:
PyObject becomes Py<PyAny>, downcast*() becomes cast*(), allow_threads()
becomes detach() and assume_gil_acquired() becomes assume_attached().

Py::from_borrowed_ptr() is deprecated, so build the parent reference in
ZstdBufferWithSegments.segments() with a safe conversion instead of a
raw pointer cast.
Build cp315 and cp315t wheels and add 3.15 and 3.15t to the test and
typing CI matrices. Like 3.14, Linux wheels for 3.15 are built with
manylinux_2_28 and musllinux_1_2 only.

Add the Python 3.15 trove classifier and list 3.15 as supported in the
docs.

@pylaterreur pylaterreur left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for this. I built and tested this on Linux x86_64 with GitHub's CPython 3.15.0 and 3.15.0t builds, the ones setup-python installs. I used test.yml's settings: uv 0.8.11, ZSTD_WARNINGS_AS_ERRORS=1, ZSTD_SLOW_TESTS=1, the hypothesis ci profile and PYTHONDEVMODE=1, with -n 2.

C CFFI Rust (PyO3 0.29.3) wheel from uv build, tested like cibuildwheel (C / CFFI)
3.15.0 292 passed 266 passed, 26 skipped 291 passed, 1 failed 248 passed, 44 skipped / 224 passed, 68 skipped
3.15.0t 292 passed 266 passed, 26 skipped 291 passed, 1 failed 248 passed, 44 skipped / 224 passed, 68 skipped

The Rust failure is test_optimize, which also fails on main. On 3.15.0t, the GIL stays disabled after importing the C or CFFI backend. No code changes were needed.

Two things in CI:

  • wheel.yml still runs cibuildwheel 4.1.0, which builds the macOS and Windows cp315/cp315t wheels against 3.15.0b2. Your fork's macos (cp315, arm64) job shows Python 3.15.0b2 (v3.15.0b2:94a64bbc6ce, ...) and windows (cp315, win_amd64) shows Python 3.15.0b2 (tags/v3.15.0b2:94a64bb, ...). cibuildwheel 4.3.1 builds against the final 3.15.0. I opened zydo#1 against your branch with that bump. A run of it builds and tests the macOS and Windows cp315/cp315t wheels against the final 3.15.0.
  • The 3.15t jobs in test.yml don't run on a free-threaded Python, and neither do the existing 3.13t/3.14t jobs. uv 0.8.11 skips the free-threaded interpreter from setup-python, so your fork's test (ubuntu-24.04, 3.15t, x64) used /usr/bin/python3 (3.12.3). The 3.15.0t results above come from the real free-threaded build. #360 fixes this on main. In a run of it, the 3.13t/3.14t jobs use python3.13t/python3.14t and pass.

Prepared with the help of Claude Code.

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.

2 participants