Skip to content

Commit 9b13f83

Browse files
authored
Add opt-in Python 3.15 / 3.15t torch-only validation (Linux x86 + aarch64) (#8241)
## What Forward-ports the **Python 3.15 / 3.15t** binary-validation enablement developed on `release/2.13` to `main`, so `main` also validates 3.15. Scoped as: - **torch only** (no torchvision -- no 3.15 torchvision wheels yet), - **Linux x86 + aarch64** only, - the **nightly and test** channels, - **opt-in**, so the shared generator default is unchanged for everyone else. Extracted from release/2.13 commits #8214, #8216, #8222, #8227, #8230, #8239 -- **excluding the release-only bits**: the `pytorch/pytorch` checkout stays `ref: main` here (release/2.13 pinned it to `ref: release/2.13` for the cuDNN-version smoke check; that fix is not applicable to main). ## Changes **`tools/scripts/generate_binary_build_matrix.py`** - New `PREVIEW_PYTHON_ARCHES_DICT` (`3.15`/`3.15t` on the **nightly and test** channels; release excluded) and `TORCH_ONLY_PYTHON_ARCHES`. `PYTHON_ARCHES_DICT` default is **unchanged**, so torchvision/torchaudio, Windows and macOS matrices are byte-for-byte identical. - `generate_wheels_matrix` appends preview versions only when `include_preview_python_versions` is enabled **and** `os in (linux, linux-aarch64)`. - `get_wheel_install_command` emits `pip3 install torch ...` (no torchvision) for the preview versions. - New `--include-preview-python-versions` flag / `INCLUDE_PREVIEW_PYTHON_VERSIONS` env. **`.github/workflows/generate_binary_build_matrix.yml`** -- new `include-preview-python-versions` input (default `disable`). **`validate-linux-binaries.yml` / `validate-aarch64-linux-binaries.yml`** -- set `include-preview-python-versions: enable` (the only change; `ref: main` untouched). **`.github/scripts/validate_binaries.sh`** - `get_python_config`: 3.15/3.15t conda-forge cases (`python_dev` + `python_rc` labels). - Provision **CPython 3.15.0b1** (`3.15.0b1+freethreaded` for 3.15t) via **uv** instead of conda for 3.15/3.15t: the cp315 wheels are built against 3.15.0b1 while conda-forge only ships 3.15.0a8, whose pre-release ABI differs and segfaults on `import torch`. - Force `TORCH_ONLY` for 3.15/3.15t (guarded with `:-` for the libtorch path). - Skip the `torch.compile` smoke check on 3.15+ (unsupported there). ## Blast radius - torchvision / torchaudio, Windows / macOS torch validation, release channel: **unaffected** (default unchanged; preview versions stripped off-channel / off-OS; release excluded). - Only torch **Linux x86 + aarch64** validation on the **nightly and test** channels gains 3.15/3.15t. ## Test plan - `python3 -m unittest tools.tests.test_generate_binary_build_matrix` -- **12 tests pass** (7 existing + 5 preview: opt-in adds 3.15/3.15t on linux & linux-aarch64 for nightly **and** test; off by default; excluded on windows/macos even when opted in; excluded on the release channel; torch-only install). - `bash -n .github/scripts/validate_binaries.sh` passes. - Verified generation delta: `--channel nightly`/`--channel test` `--operating-system linux` gains exactly `3.15`/`3.15t` only with `--include-preview-python-versions enable`; `--channel release` never does. ## Note `black 25.11.0` locally wants to collapse one pre-existing wrapped line (`whl_install_command` for winarm64) that this PR does not touch; left as-is to match `main`'s current (lint-passing) formatting -- it's a black-version skew, not a change here.
1 parent 14627f6 commit 9b13f83

6 files changed

Lines changed: 178 additions & 7 deletions

.github/scripts/validate_binaries.sh

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ get_python_config() {
2727
PYTHON_V=3.14.0rc1
2828
CONDA_EXTRA_PARAM=" -c conda-forge/label/python_rc -c conda-forge"
2929
;;
30+
# Note: 3.15 / 3.15t are intentionally absent here. They are provisioned
31+
# via uv (CPython 3.15.0b1) in the interpreter-setup branch below and
32+
# never reach the conda create path that reads CONDA_EXTRA_PARAM.
3033
*)
3134
PYTHON_V=${MATRIX_PYTHON_VERSION}
3235
CONDA_EXTRA_PARAM=""
@@ -185,13 +188,20 @@ run_smoke_tests() {
185188
source "${SCRIPT_DIR}/validate_test_ops.sh"
186189
fi
187190

191+
# torch.compile is not supported on Python 3.15+ (torch.compile() raises
192+
# RuntimeError at call time), so disable the compile smoke test there.
193+
local compile_check=""
194+
if [[ ${MATRIX_PYTHON_VERSION} == "3.15" || ${MATRIX_PYTHON_VERSION} == "3.15t" ]]; then
195+
compile_check="--torch-compile-check disabled"
196+
fi
197+
188198
# Regular smoke test
189-
${PYTHON_RUN} ./smoke_test/smoke_test.py ${test_suffix}
199+
${PYTHON_RUN} ./smoke_test/smoke_test.py ${test_suffix} ${compile_check}
190200

191201
# For pip install also test with latest numpy
192202
if [[ ${MATRIX_PACKAGE_TYPE} == 'wheel' ]]; then
193203
pip3 install numpy --upgrade --force-reinstall
194-
${PYTHON_RUN} ./smoke_test/smoke_test.py ${test_suffix}
204+
${PYTHON_RUN} ./smoke_test/smoke_test.py ${test_suffix} ${compile_check}
195205
fi
196206

197207
popd
@@ -219,6 +229,14 @@ cleanup_conda_env() {
219229

220230
handle_aarch64_cuda_override
221231

232+
# torchvision wheels are not published for Python 3.15 / 3.15t yet, so validate
233+
# torch only: skip the torchvision install and its smoke-test module check.
234+
# Guard with :- since libtorch builds run this before the libtorch exit below
235+
# and do not set MATRIX_PYTHON_VERSION (set -u would abort otherwise).
236+
if [[ ${MATRIX_PYTHON_VERSION:-} == "3.15" || ${MATRIX_PYTHON_VERSION:-} == "3.15t" ]]; then
237+
export TORCH_ONLY=true
238+
fi
239+
222240
if [[ ${MATRIX_PACKAGE_TYPE} == "libtorch" ]]; then
223241
LIBTORCH_PYTHON="python3"
224242
if [[ ${TARGET_OS} == 'windows' ]]; then
@@ -237,11 +255,30 @@ if [[ ${TARGET_OS} == 'windows' ]]; then
237255
export PYTHON_RUN="python"
238256
fi
239257

240-
# Setup conda environment
241-
update_conda
242-
get_python_config
243-
conda create -y -n "${ENV_NAME}" python="${PYTHON_V}" pip ${CONDA_EXTRA_PARAM}
244-
conda activate "${ENV_NAME}"
258+
# Setup the Python environment.
259+
#
260+
# Python 3.15 is still pre-release. The cp315/cp315t wheels are built against
261+
# CPython 3.15.0b1 (see pytorch .ci/docker/common/install_cpython.sh), but
262+
# conda-forge only ships 3.15.0a8 -- the pre-release ABI differs, so installing
263+
# the wheel under the conda interpreter segfaults on "import torch". Provision
264+
# the matching 3.15.0b1 interpreter with uv (from python-build-standalone)
265+
# instead of conda. A --seed venv provides pip so the rest of the flow (pip3
266+
# install, smoke tests) is unchanged.
267+
if [[ ${MATRIX_PYTHON_VERSION} == "3.15" || ${MATRIX_PYTHON_VERSION} == "3.15t" ]]; then
268+
UV_PYTHON="3.15.0b1"
269+
if [[ ${MATRIX_PYTHON_VERSION} == "3.15t" ]]; then
270+
UV_PYTHON="3.15.0b1+freethreaded"
271+
fi
272+
curl -LsSf https://astral.sh/uv/install.sh | sh
273+
source "${HOME}/.local/bin/env"
274+
uv venv --seed --python "${UV_PYTHON}" "${ENV_NAME}"
275+
source "${ENV_NAME}/bin/activate"
276+
else
277+
update_conda
278+
get_python_config
279+
conda create -y -n "${ENV_NAME}" python="${PYTHON_V}" pip ${CONDA_EXTRA_PARAM}
280+
conda activate "${ENV_NAME}"
281+
fi
245282

246283
# Save original PATH for macos-arm64 workaround
247284
export OLD_PATH=${PATH}

.github/workflows/generate_binary_build_matrix.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ on:
5252
description: "A JSON-encoded list of python versions to build. An empty list means building all supported versions"
5353
default: "[]"
5454
type: string
55+
include-preview-python-versions:
56+
description: "Include opt-in preview python versions (torch-only, Linux x86 and aarch64), e.g. 3.15/3.15t"
57+
default: "disable"
58+
required: false
59+
type: string
5560
getting-started:
5661
description: "Release matrix for getting started page"
5762
required: false
@@ -97,6 +102,7 @@ jobs:
97102
BUILD_PYTHON_ONLY: ${{ inputs.build-python-only }}
98103
GETTING_STARTED: ${{ inputs.getting-started }}
99104
PYTHON_VERSIONS: ${{ inputs.python-versions }}
105+
INCLUDE_PREVIEW_PYTHON_VERSIONS: ${{ inputs.include-preview-python-versions }}
100106
run: |
101107
set -eou pipefail
102108
MATRIX_BLOB="$(python3 tools/scripts/generate_binary_build_matrix.py)"

.github/workflows/validate-aarch64-linux-binaries.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ jobs:
9999
with-cuda: enable
100100
with-cpu: enable
101101
use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }}
102+
# Validate torch-only preview python versions (3.15/3.15t) on the nightly and test channels.
103+
include-preview-python-versions: enable
102104

103105
linux-aarch64:
104106
needs: generate-aarch64-linux-matrix

.github/workflows/validate-linux-binaries.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ jobs:
108108
channel: ${{ inputs.channel }}
109109
use-only-dl-pytorch-org: ${{ inputs.use-only-dl-pytorch-org }}
110110
with-xpu: enable
111+
# Validate torch-only preview python versions (3.15/3.15t) on the nightly and test channels.
112+
include-preview-python-versions: enable
111113

112114
linux:
113115
needs: generate-linux-matrix

tools/scripts/generate_binary_build_matrix.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@
2727
"release": ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"],
2828
}
2929

30+
# Preview Python versions validated for torch only, on Linux x86 and aarch64.
31+
# These are opt-in via INCLUDE_PREVIEW_PYTHON_VERSIONS so that the shared
32+
# generator default is unchanged for domain libraries (torchvision, torchaudio)
33+
# and for Windows/macOS, which do not have wheels for these versions yet.
34+
PREVIEW_PYTHON_ARCHES_DICT = {
35+
"nightly": ["3.15", "3.15t"],
36+
"test": ["3.15", "3.15t"],
37+
"release": [],
38+
}
39+
40+
# Python versions for which only torch is validated (no torchvision). torchvision
41+
# wheels are not published for these versions yet, so the install command must
42+
# request torch alone.
43+
TORCH_ONLY_PYTHON_ARCHES = ["3.15", "3.15t"]
44+
3045
MACOS_PYTHON_POINT_VERSIONS = {
3146
"3.10": "3.10.19",
3247
"3.11": "3.11.14",
@@ -293,6 +308,11 @@ def get_wheel_install_command(
293308
else PACKAGES_TO_INSTALL_WHL
294309
)
295310

311+
# Validate torch only (no torchvision) for versions without published
312+
# torchvision wheels, e.g. 3.15 / 3.15t.
313+
if python_version in TORCH_ONLY_PYTHON_ARCHES:
314+
PACKAGES_TO_INSTALL = "torch"
315+
296316
if (
297317
channel == RELEASE
298318
and (not use_only_dl_pytorch_org)
@@ -330,7 +350,9 @@ def generate_libtorch_matrix(
330350
abi_versions: Optional[List[str]] = None,
331351
arches: Optional[List[str]] = None,
332352
libtorch_variants: Optional[List[str]] = None,
353+
include_preview_python_versions: bool = False,
333354
) -> List[Dict[str, str]]:
355+
# libtorch is python-agnostic; the preview python versions do not apply.
334356
ret: List[Dict[str, str]] = []
335357

336358
if arches is None:
@@ -422,13 +444,21 @@ def generate_wheels_matrix(
422444
getting_started: bool = False,
423445
python_versions: Optional[List[str]] = None,
424446
arches: Optional[List[str]] = None,
447+
include_preview_python_versions: bool = False,
425448
) -> List[Dict[str, str]]:
426449
package_type = "wheel"
427450

428451
if not python_versions:
429452
# Define default python version
430453
python_versions = list(PYTHON_ARCHES)
431454

455+
# Opt-in preview versions (e.g. 3.15/3.15t) are torch-only and validated
456+
# on Linux x86 and aarch64 only. Append them to the default list so the
457+
# shared default (used by domain libraries, Windows and macOS) is
458+
# unaffected.
459+
if include_preview_python_versions and os in (LINUX, LINUX_AARCH64):
460+
python_versions += PREVIEW_PYTHON_ARCHES_DICT.get(channel, [])
461+
432462
if os == WINDOWS_ARM64:
433463
python_versions = ["3.11", "3.12", "3.13"] # only versions for now
434464

@@ -528,6 +558,7 @@ def generate_build_matrix(
528558
build_python_only: str,
529559
getting_started: str = "false",
530560
python_versions: Optional[List[str]] = None,
561+
include_preview_python_versions: str = "disable",
531562
) -> Dict[str, List[Dict[str, str]]]:
532563
includes = []
533564

@@ -557,6 +588,8 @@ def generate_build_matrix(
557588
use_only_dl_pytorch_org == "true",
558589
getting_started == "true",
559590
python_versions,
591+
include_preview_python_versions=include_preview_python_versions
592+
== ENABLE,
560593
)
561594
)
562595

@@ -658,6 +691,15 @@ def main(args: List[str]) -> None:
658691
default=os.getenv("PYTHON_VERSIONS", "[]"),
659692
)
660693

694+
parser.add_argument(
695+
"--include-preview-python-versions",
696+
help="Include opt-in preview python versions (torch-only, Linux x86 and "
697+
"aarch64) in the matrix",
698+
type=str,
699+
choices=[ENABLE, DISABLE],
700+
default=os.getenv("INCLUDE_PREVIEW_PYTHON_VERSIONS", DISABLE),
701+
)
702+
661703
options = parser.parse_args(args)
662704
try:
663705
python_versions = json.loads(options.python_versions)
@@ -681,6 +723,7 @@ def main(args: List[str]) -> None:
681723
options.build_python_only,
682724
options.getting_started,
683725
python_versions,
726+
options.include_preview_python_versions,
684727
)
685728

686729
print(json.dumps(build_matrix))

tools/tests/test_generate_binary_build_matrix.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,87 @@ def test_linux_wheel_cuda_xpu_nocpu(self):
126126
reference_output_file="build_matrix_linux_wheel_xpu.json",
127127
)
128128

129+
def _test_channel_python_versions(
130+
self,
131+
operating_system: str,
132+
include_preview: str = "disable",
133+
channel: str = "test",
134+
) -> set:
135+
out = generate_build_matrix(
136+
"wheel",
137+
operating_system,
138+
channel,
139+
"enable",
140+
"enable" if operating_system in ("linux",) else "disable",
141+
"enable",
142+
"enable" if operating_system in ("linux", "windows") else "disable",
143+
"false",
144+
"false",
145+
"disable",
146+
"false",
147+
None,
148+
include_preview,
149+
)
150+
return {entry["python_version"] for entry in out["include"]}
151+
152+
def test_preview_python_versions_opt_in_on_linux(self):
153+
# 3.15 / 3.15t are validated on Linux x86 and aarch64 for the nightly and
154+
# test channels when explicitly opted in.
155+
for channel in ("nightly", "test"):
156+
for operating_system in ("linux", "linux-aarch64"):
157+
versions = self._test_channel_python_versions(
158+
operating_system, include_preview="enable", channel=channel
159+
)
160+
self.assertIn("3.15", versions)
161+
self.assertIn("3.15t", versions)
162+
163+
def test_preview_python_versions_off_by_default(self):
164+
# Without opt-in the shared default is unchanged (e.g. torchvision builds).
165+
for operating_system in ("linux", "linux-aarch64"):
166+
versions = self._test_channel_python_versions(operating_system)
167+
self.assertNotIn("3.15", versions)
168+
self.assertNotIn("3.15t", versions)
169+
170+
def test_preview_python_versions_excluded_on_non_linux(self):
171+
# Windows and macOS must not pick up the preview versions even when opted in.
172+
for operating_system in ("windows", "macos"):
173+
versions = self._test_channel_python_versions(
174+
operating_system, include_preview="enable"
175+
)
176+
self.assertNotIn("3.15", versions)
177+
self.assertNotIn("3.15t", versions)
178+
179+
def test_preview_python_versions_excluded_on_release_channel(self):
180+
# Preview versions are defined for the nightly and test channels only,
181+
# never for the release channel.
182+
versions = self._test_channel_python_versions(
183+
"linux", include_preview="enable", channel="release"
184+
)
185+
self.assertNotIn("3.15", versions)
186+
self.assertNotIn("3.15t", versions)
187+
188+
def test_torch_only_install_command_for_preview_arches(self):
189+
out = generate_build_matrix(
190+
"wheel",
191+
"linux",
192+
"test",
193+
"enable",
194+
"enable",
195+
"enable",
196+
"enable",
197+
"false",
198+
"false",
199+
"disable",
200+
"false",
201+
None,
202+
"enable",
203+
)
204+
for entry in out["include"]:
205+
if entry["python_version"] in ("3.15", "3.15t"):
206+
# torchvision is not published for these versions yet.
207+
self.assertNotIn("torchvision", entry["installation"])
208+
self.assertIn("torch", entry["installation"])
209+
129210

130211
def parse_args():
131212
parser = argparse.ArgumentParser(description="Test generate build matrix")

0 commit comments

Comments
 (0)