From d1e68221814dc8acb6f3ecd73167f827aff92b81 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Aug 2026 16:05:08 +0000 Subject: [PATCH] Test and build wheels on Python 3.15 Adds Python 3.15 to the test matrix in the same shape a stable version gets -- Cython on and off, plus a confluent-driver leg -- and moves the release wheel build from cibuildwheel 4.1.0 to 4.2.0, the first release that knows about CPython 3.15. 4.2.0 builds cp315 by default: cibuildwheel gates a Python behind the `cpython-prerelease` enable group only until its first release candidate, because the ABI is frozen at rc1, so a wheel built against 3.15.0rc1 is forward-compatible with 3.15.0 final. The existing `cp31?t-*` skip keeps this to cp315 without cp315t, matching the other versions. `allow-prereleases` on setup-python resolves `3.15` to the newest pre-release the runner image publishes (3.15.0rc1 today) and will pick up 3.15.0 final with no further change. It leaves the stable rows alone: it widens `3.X` to `~3.X.0-0`, and a pre-release only wins when no stable release satisfies the spec, so 3.10-3.14 still resolve to their newest stable patch. The 3.15 rows are advisory (`experimental: true` -> `continue-on-error`) and will stay red for now. faust does not import on 3.15 until mode-streaming ships the fix for `typing._eval_type`, whose `type_params` argument became a required positional in 3.15: every Record model resolves its annotations through `mode.utils.objects.annotations`, so collection dies before the first test runs. With that fix applied locally the suite is clean on 3.15.0rc1 -- 2207 passed in unit+functional, 17 in integration/meticulous/regression, bandit clean, Cython extensions built. The workflow comment records what to change once a mode-streaming release carries the fix. tox.ini's envlist stopped at 3.12; it now mirrors the CI matrix. Its basepython block repeated the whole check-env list on all three lines, so those factors matched every line at once and tox resolved `base_python` to nothing, leaving flake8/typecheck/bandit and friends on whatever interpreter happened to be running tox. They now pin to 3.12, the PYTHON_LATEST the lint job uses. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jr6Lsfd3fULmP1Gi4b6nDK --- .github/workflows/python-package.yml | 46 +++++++++++++++++++++++++++- pyproject.toml | 1 + tox.ini | 22 ++++++++++--- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 79fb3b54a..5bd99a7ce 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -79,6 +79,35 @@ jobs: # a missing confluent_kafka -- actually run. kafka-driver: ['aiokafka'] include: + # Python 3.15. `allow-prereleases` below resolves this to whatever + # pre-release the runner image publishes -- 3.15.0rc1 at the time of + # writing, and 3.15.0 final once it ships, with no change needed + # here. Same shape as a stable row: Cython on and off, plus a + # confluent leg. + # + # Advisory (`experimental: true` -> `continue-on-error`) for two + # reasons. The pre-release one is the same as everywhere else: rc2 + # and final are still to come. The specific one is that faust does + # not import at all on 3.15 until mode-streaming ships the fix for + # `typing._eval_type`, whose `type_params` argument became a required + # positional in 3.15 -- every faust Record model resolves its + # annotations through `mode.utils.objects.annotations`, so collection + # dies before the first test runs. The fix is on mode's master; this + # leg goes green once a mode-streaming release carries it (bump the + # floor in requirements/requirements.txt then, and move these entries + # into the matrix above to make 3.15 required). + - python-version: '3.15' + use-cython: 'true' + experimental: true + kafka-driver: 'aiokafka' + - python-version: '3.15' + use-cython: 'false' + experimental: true + kafka-driver: 'aiokafka' + - python-version: '3.15' + use-cython: 'false' + experimental: true + kafka-driver: 'confluent' # confluent driver: broker-less unit tests over a pure-Python # wrapper, so one leg per Python version (Cython off) is enough. - python-version: '3.10' @@ -111,6 +140,13 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + # Required by the 3.15 rows: a bare `3.15` matches stable releases + # only and fails with "Version 3.15 was not found in the local + # cache". Safe for every other row -- it widens `3.X` to + # `~3.X.0-0`, and a pre-release only wins when no stable release + # satisfies the spec, so 3.10-3.14 still resolve to their newest + # stable patch. + allow-prereleases: true cache: pip # See the lint job: test.txt alone leaves the key unchanged when the # transitively-included pins move, and the confluent legs below @@ -317,7 +353,15 @@ jobs: # cibuildwheel 2.21.3 predates CPython 3.14, so `build = "cp3*"` # stopped at cp313 and no 3.14 wheels were published (issue #715). # 4.x builds 3.14 by default and still supports cp310-cp313. - uses: pypa/cibuildwheel@v4.1.0 + # + # 4.2.0 is the first pin that knows about CPython 3.15, and it builds + # cp315 by default -- no `CIBW_ENABLE: cpython-prerelease` needed, + # because cibuildwheel gates a Python behind that group only until its + # first release candidate (the ABI is frozen at rc1, so a wheel built + # against 3.15.0rc1 is forward-compatible with 3.15.0 final). The + # `cp31?t-*` skip in pyproject.toml's [tool.cibuildwheel] keeps this + # to cp315, without cp315t, matching the other versions. + uses: pypa/cibuildwheel@v4.2.0 - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }} diff --git a/pyproject.toml b/pyproject.toml index ee1c27d56..75110af34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Programming Language :: Python :: Implementation :: CPython", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", diff --git a/tox.ini b/tox.ini index e1673e689..4f24c8131 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,8 @@ [tox] -envlist = 3.12,3.11,3.10,flake8,apicheck,configcheck,typecheck,docstyle,bandit,spell +# Version envs mirror the CI matrix in +# .github/workflows/python-package.yml. 3.15 is still a pre-release; tox +# skips it unless a 3.15 interpreter is on PATH. +envlist = 3.15,3.14,3.13,3.12,3.11,3.10,flake8,apicheck,configcheck,typecheck,docstyle,bandit,spell [testenv] deps= @@ -16,10 +19,21 @@ sitepackages = False recreate = False commands = py.test --random-order --open-files -xvv --cov=faust tests/unit tests/functional tests/integration tests/meticulous/ tests/regression +# One interpreter per version env, and a single pinned one for the checks. +# The previous form repeated the whole check list (flake8, typecheck, ...) on +# all three lines, so every one of those factors matched all three at once and +# tox resolved `base_python` to nothing -- leaving the checks on whatever +# interpreter happened to be running tox. That is silently version-dependent +# for `typecheck`. They now pin to 3.12, the PYTHON_LATEST the CI workflow +# runs its lint job on. basepython = - 3.12,flake8,apicheck,linkcheck,configcheck,typecheck,docstyle,bandit,spell: python3.12 - 3.11,flake8,apicheck,linkcheck,configcheck,typecheck,docstyle,bandit,spell: python3.11 - 3.10,flake8,apicheck,linkcheck,configcheck,typecheck,docstyle,bandit,spell: python3.10 + 3.15: python3.15 + 3.14: python3.14 + 3.13: python3.13 + 3.12: python3.12 + 3.11: python3.11 + 3.10: python3.10 + flake8,apicheck,linkcheck,configcheck,typecheck,docstyle,bandit,spell: python3.12 [testenv:apicheck] setenv =