ci: test Python 3.15 (#6107)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d7e9e86..2ad1178 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -53,6 +53,9 @@
- runs-on: ubuntu-latest
python-version: 'graalpy-25.0'
cmake-args: -DCMAKE_CXX_STANDARD=20
+ - runs-on: ubuntu-latest
+ python-version: '3.15'
+ cmake-args: -DCMAKE_CXX_STANDARD=20
- runs-on: macos-latest
python-version: '3.14'
cmake-args: -DCMAKE_CXX_STANDARD=14
@@ -86,6 +89,9 @@
- runs-on: ubuntu-latest
python-version: '3.14'
cmake-args: -DCMAKE_CXX_STANDARD=14
+ - runs-on: ubuntu-latest
+ python-version: '3.15t'
+ cmake-args: -DCMAKE_CXX_STANDARD=17 -DPYBIND11_TEST_SMART_HOLDER=ON
# No SciPy for macOS ARM
- runs-on: macos-15-intel
@@ -101,6 +107,9 @@
python-version: '3.14t'
cmake-args: -DCMAKE_CXX_STANDARD=20
- runs-on: macos-latest
+ python-version: '3.15'
+ cmake-args: -DCMAKE_CXX_STANDARD=17
+ - runs-on: macos-latest
python-version: 'pypy-3.11-v7.3.23'
- runs-on: macos-latest
python-version: 'graalpy-25.0'
@@ -128,6 +137,9 @@
python-version: '3.14t'
cmake-args: -DCMAKE_CXX_STANDARD=23
- runs-on: windows-latest
+ python-version: '3.15'
+ cmake-args: -DCMAKE_CXX_STANDARD=20
+ - runs-on: windows-latest
python-version: 'pypy3.11'
cmake-args: -DCMAKE_CXX_STANDARD=20
# The setup-python action currently doesn't have graalpy for windows
diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml
index 9639d26..d27af50 100644
--- a/.github/workflows/upstream.yml
+++ b/.github/workflows/upstream.yml
@@ -18,7 +18,7 @@
jobs:
standard:
- name: "🐍 3.13 latest • ubuntu-latest • x64"
+ name: "🐍 3.15 latest • ubuntu-latest • x64"
runs-on: ubuntu-latest
# Only runs when the 'python dev' label is selected
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
@@ -26,10 +26,10 @@
steps:
- uses: actions/checkout@v7
- - name: Setup Python 3.13
+ - name: Setup Python 3.15
uses: actions/setup-python@v6
with:
- python-version: "3.13"
+ python-version: "3.15"
allow-prereleases: true
- name: Setup Boost
diff --git a/pyproject.toml b/pyproject.toml
index 03214c6..c3395aa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,6 +23,7 @@
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
+ "Programming Language :: Python :: 3.15",
"Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: C++",
diff --git a/tests/requirements.txt b/tests/requirements.txt
index 53f05b6..1ba4026 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,6 +1,8 @@
--extra-index-url=https://www.graalvm.org/python/wheels
--only-binary=:all:
build>=1
+# No released numpy has cp315 wheels yet; drop the upper bound once it does.
+numpy>=2.4.0; platform_python_implementation=="CPython" and python_version>="3.14" and python_version<"3.15"
numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy"
numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=="PyPy"
numpy~=2.2.0; python_version=="3.10" and platform_python_implementation=="PyPy"
@@ -10,7 +12,6 @@
numpy~=1.26.0; platform_python_implementation=="CPython" and python_version>="3.11" and python_version<"3.13" and platform_machine!="ARM64"
numpy>=2.3.0; platform_python_implementation=="CPython" and python_version>="3.11" and platform_machine=="ARM64"
numpy~=2.2.0; platform_python_implementation=="CPython" and python_version=="3.13" and platform_machine!="ARM64"
-numpy>=2.4.0; platform_python_implementation=="CPython" and python_version>="3.14"
pytest>=6
pytest-timeout
scipy~=1.5.4; platform_python_implementation=="CPython" and python_version<"3.10"
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index e6441f6..1ee13ba 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import re
import sys
import pytest
@@ -123,6 +124,8 @@
assert triggered is True
_, captured_stderr = capsys.readouterr()
+ # Strip ANSI codes that Python 3.15+ adds to this traceback under FORCE_COLOR.
+ captured_stderr = re.sub(r"\x1b\[[0-?]*[ -/]*m", "", captured_stderr)
assert captured_stderr.startswith("Exception ignored in: 'already_set demo'")
assert captured_stderr.rstrip().endswith("KeyError: 'bar'")