Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c43ff6f
chore: fail fast and clearly when Py_LIMITED_API is defined
henryiii Oct 7, 2026
a07e4ed
chore: add a stable-ABI tag to PYBIND11_INTERNALS_ID and PYBIND11_MOD…
henryiii Oct 7, 2026
8d8fe02
refactor: add detail::get_bases()/get_mro() and use them instead of t…
henryiii Oct 7, 2026
ac39419
refactor: add detail::get_tp_name()/tp_name_equals() and use them ins…
henryiii Oct 7, 2026
e95a78a
fix: use the internals registry in type_is_managed_by_our_internals()
henryiii Oct 7, 2026
c7d6c4e
refactor: walk tracebacks by attribute in error_already_set::what()
henryiii Oct 7, 2026
c3c93a8
refactor: add detail::type_lookup() and use it instead of _PyType_Lookup
henryiii Oct 7, 2026
7b35af8
refactor: use PyThreadState_GetInterpreter() instead of tstate->interp
henryiii Oct 7, 2026
c800cc1
refactor: add PYBIND11_HAS_DIRECT_STRUCT_ACCESS for the struct-access…
henryiii Oct 7, 2026
cb3611c
refactor: replace PyTuple/PyList/PyByteArray/PyCFunction macro accessors
henryiii Oct 7, 2026
600c4a4
feat: add the PYBIND11_TYPE_CREATION_VIA_SPEC opt-in macro and CI job
henryiii Oct 7, 2026
7f95267
refactor: forward to type/property slots through PyType_GetSlot()
henryiii Oct 7, 2026
cfb5a74
feat: spec-based pybind11_static_property behind PYBIND11_TYPE_CREATI…
henryiii Oct 7, 2026
b2649fd
feat: spec-based pybind11_type metaclass behind PYBIND11_TYPE_CREATIO…
henryiii Oct 7, 2026
d6e3f64
feat: spec-based pybind11_object base behind PYBIND11_TYPE_CREATION_V…
henryiii Oct 7, 2026
0528f8a
feat: spec-based make_new_python_type() behind PYBIND11_TYPE_CREATION…
henryiii Oct 7, 2026
408c543
refactor: wrap instance tp_alloc/tp_free and the __dict__ slot pointer
henryiii Oct 7, 2026
fd57174
feat: support the Python stable ABI (Py_LIMITED_API >= 0x030C0000)
henryiii Oct 7, 2026
4802878
feat(cmake): add STABLE_ABI to pybind11_add_module()
henryiii Oct 7, 2026
bd5a17b
feat(setup_helpers): Pybind11Extension(py_limited_api=True)
henryiii Oct 7, 2026
8ac98c9
ci: build and test the suite for the stable ABI
henryiii Oct 7, 2026
ee6d656
docs: describe stable ABI (abi3) support
henryiii Oct 7, 2026
a221b18
fix(cmake): export SABIModule target and Python_SOSABI to callers
henryiii Oct 7, 2026
2024bd3
feat: support the free-threaded stable ABI (abi3t, CPython 3.15+)
henryiii Oct 8, 2026
d7ade07
feat(numpy): support numpy.h under the abi3t stable ABI
henryiii Oct 8, 2026
84cedff
feat(chrono): support chrono.h under the stable ABI
henryiii Oct 8, 2026
d34635c
perf(stable-abi): use the default type getattro under the limited API
henryiii Oct 8, 2026
2dd584b
perf(stable-abi): bind methods with PyObject_Vectorcall
henryiii Oct 8, 2026
67b5456
refactor(stable-abi): share type attribute, slot and spec helpers
henryiii Oct 8, 2026
5544d87
fix(stable-abi): gate 3.13+ API use on Py_LIMITED_API, not the headers
henryiii Oct 8, 2026
09de9c1
perf(stable-abi): make instancemethod a vectorcall method descriptor
henryiii Oct 8, 2026
691f0bd
fix(chrono): import the datetime C API per translation unit
henryiii Oct 8, 2026
6c99d85
fix(stable-abi): keep instance lookup safe across critical-section su…
henryiii Oct 8, 2026
0667f6a
test(stable-abi): check the method-descriptor flag only under the lim…
henryiii Oct 8, 2026
7084836
fix(cmake): do not link libpython into SHARED STABLE_ABI libraries
henryiii Oct 8, 2026
1efa690
ci(stable-abi): resolve forward-test ignores relative to build/tests
henryiii Oct 8, 2026
2fac1f5
style: pre-commit fixes
pre-commit-ci[bot] Oct 8, 2026
569c3b1
fix(pypy): use the accessors PyPy provides
henryiii Oct 8, 2026
d86ba7d
fix: MSVC C4456 shadowing and clang-tidy macro parentheses
henryiii Oct 8, 2026
22ae75b
feat: build abi3t from GIL-enabled Python; fix stable ABI CI failures
henryiii Oct 8, 2026
78233ab
fix(stable-abi): Windows abi3t export symbol, MSVC C4702, .pyd name c…
henryiii Oct 8, 2026
f53e975
feat: make PyType_FromMetaclass the default type-creation path on CPy…
henryiii Oct 8, 2026
2c1c613
docs: describe the scikit-build-core stable ABI setup
henryiii Oct 8, 2026
75be228
fix(stable-abi): gate PyDict_*Ref helpers on the limited API version
henryiii Oct 8, 2026
f7103aa
fix(stable-abi): address review findings
henryiii Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,269 @@ jobs:
python-version: ${{ matrix.python-version }}
cmake-args: -DPYBIND11_TEST_PRECOMPILE=ON

# Opt-in PyType_FromMetaclass type creation (the future default on CPython 3.12+
# and the only path under the stable ABI).
type-creation-legacy:
if: github.event.pull_request.draft == false
strategy:
fail-fast: false
matrix:
include:
- runs-on: ubuntu-latest
python-version: '3.12'
cmake-args: -DPYBIND11_TYPE_CREATION_VIA_SPEC=OFF
- runs-on: ubuntu-latest
python-version: '3.14'
cmake-args: -DPYBIND11_TYPE_CREATION_VIA_SPEC=OFF -DPYBIND11_TEST_PRECOMPILE=ON
- runs-on: macos-latest
python-version: '3.13'
cmake-args: -DPYBIND11_TYPE_CREATION_VIA_SPEC=OFF
- runs-on: windows-2022
python-version: '3.13'
cmake-args: -DPYBIND11_TYPE_CREATION_VIA_SPEC=OFF

name: 📐
uses: ./.github/workflows/reusable-standard.yml
with:
runs-on: ${{ matrix.runs-on }}
python-version: ${{ matrix.python-version }}
cmake-args: ${{ matrix.cmake-args }}

# Stable ABI (Py_LIMITED_API): every test module is built as *.abi3.
stable-abi:
if: github.event.pull_request.draft == false
strategy:
fail-fast: false
matrix:
include:
- runs-on: ubuntu-latest
python-version: '3.12'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON
# precompiled: the -inl.h files must not read any non-limited struct field
- runs-on: ubuntu-latest
python-version: '3.14'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON -DPYBIND11_TEST_PRECOMPILE=ON
# A newer limited API target: gates must follow the limited API, not PY_VERSION_HEX
- runs-on: ubuntu-latest
python-version: '3.14'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON -DPYBIND11_STABLE_ABI_VERSION=3.14
- runs-on: macos-latest
python-version: '3.13'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON -DCMAKE_CXX_STANDARD=17
- runs-on: windows-2022
python-version: '3.12'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON
# free-threaded stable ABI (abi3t, PEP 803): PyObject is opaque
- runs-on: ubuntu-latest
python-version: '3.15t'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON
- runs-on: macos-latest
python-version: '3.15t'
cmake-args: -DPYBIND11_TEST_STABLE_ABI=ON -DPYBIND11_TEST_PRECOMPILE=ON

name: 🧱
uses: ./.github/workflows/reusable-standard.yml
with:
runs-on: ${{ matrix.runs-on }}
python-version: ${{ matrix.python-version }}
cmake-args: ${{ matrix.cmake-args }}

# The point of the stable ABI: build the suite once against the 3.12 headers
# and run the same .abi3 modules on newer interpreters.
stable-abi-forward:
if: github.event.pull_request.draft == false
name: 🧱 abi3 on newer Pythons
runs-on: ubuntu-latest
timeout-minutes: 60
env:
# Relative to the working directory of the forward steps, build/tests
PYBIND11_STABLE_ABI_SKIPPED_TESTS: >-
--ignore=../../tests/test_custom_type_setup.py
--ignore=../../tests/test_multiple_interpreters.py
--ignore=../../tests/test_class_release_gil_before_calling_cpp_dtor.py

steps:
- uses: actions/checkout@v7

- name: Setup Python 3.12 (build)
id: py312
uses: actions/setup-python@v7
with:
python-version: '3.12'

- name: Setup Python 3.13 and 3.14 (run)
uses: actions/setup-python@v7
with:
python-version: |
3.13
3.14
allow-prereleases: true

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true

- name: Prepare env
run: |
for py in "${{ steps.py312.outputs.python-path }}" python3.13 python3.14; do
uv pip install --python="$py" --system -r tests/requirements.txt
done

- name: Configure (Python 3.12, stable ABI)
run: >
cmake -S. -Bbuild -Werror=dev -GNinja
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DPYBIND11_TEST_STABLE_ABI=ON
-DPYTHON_EXECUTABLE=${{ steps.py312.outputs.python-path }}

- name: Build
run: cmake --build build

- name: Python tests (3.12)
run: cmake --build build --target pytest

- name: Python tests (3.13, same binaries)
working-directory: build/tests
run: python3.13 -m pytest -v ../../tests ${{ env.PYBIND11_STABLE_ABI_SKIPPED_TESTS }}

- name: Python tests (3.14, same binaries)
working-directory: build/tests
run: python3.14 -m pytest -v ../../tests ${{ env.PYBIND11_STABLE_ABI_SKIPPED_TESTS }}

# abi3t modules also load on GIL-enabled interpreters: build the suite with
# 3.15t and run the same .abi3t modules on 3.15.
stable-abi-forward-ft:
if: github.event.pull_request.draft == false
name: 🧱 abi3t on GIL-enabled Python
runs-on: ubuntu-latest
timeout-minutes: 60
env:
# Relative to the working directory of the forward steps, build/tests
PYBIND11_STABLE_ABI_SKIPPED_TESTS: >-
--ignore=../../tests/test_custom_type_setup.py
--ignore=../../tests/test_multiple_interpreters.py
--ignore=../../tests/test_class_release_gil_before_calling_cpp_dtor.py

steps:
- uses: actions/checkout@v7

- name: Setup Python 3.15t (build)
id: py315t
uses: actions/setup-python@v7
with:
python-version: '3.15t'
allow-prereleases: true

- name: Setup Python 3.15 (run)
uses: actions/setup-python@v7
with:
python-version: '3.15'
allow-prereleases: true

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true

- name: Prepare env
run: |
for py in "${{ steps.py315t.outputs.python-path }}" python3.15; do
uv pip install --python="$py" --system -r tests/requirements.txt
done

- name: Configure (Python 3.15t, stable ABI)
run: >
cmake -S. -Bbuild -Werror=dev -GNinja
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DPYBIND11_TEST_STABLE_ABI=ON
-DPYTHON_EXECUTABLE=${{ steps.py315t.outputs.python-path }}

- name: Build
run: cmake --build build

- name: Python tests (3.15t)
run: cmake --build build --target pytest

- name: Python tests (3.15, same binaries)
working-directory: build/tests
run: python3.15 -m pytest -v ../../tests ${{ env.PYBIND11_STABLE_ABI_SKIPPED_TESTS }}

# PYBIND11_ABI3T builds abi3t from GIL-enabled Python; run the same modules on 3.15t.
stable-abi-abi3t-from-gil:
if: github.event.pull_request.draft == false
strategy:
fail-fast: false
matrix:
runs-on: [ubuntu-latest, windows-latest]
name: 🧱 abi3t from GIL-enabled Python • ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 60
defaults:
run:
shell: bash
env:
# Relative to the working directory of the forward steps, build/tests
PYBIND11_STABLE_ABI_SKIPPED_TESTS: >-
--ignore=../../tests/test_custom_type_setup.py
--ignore=../../tests/test_multiple_interpreters.py
--ignore=../../tests/test_class_release_gil_before_calling_cpp_dtor.py

steps:
- uses: actions/checkout@v7

- name: Setup Python 3.15t (run)
id: py315t
uses: actions/setup-python@v7
with:
python-version: '3.15t'
allow-prereleases: true

- name: Setup Python 3.15 (build)
id: py315
uses: actions/setup-python@v7
with:
python-version: '3.15'
allow-prereleases: true

- name: Install uv
uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true

- name: Prepare env
run: |
for py in "${{ steps.py315t.outputs.python-path }}" "${{ steps.py315.outputs.python-path }}"; do
uv pip install --python="$py" --system -r tests/requirements.txt
done

- name: Configure (Python 3.15, abi3t)
run: >
cmake -S. -Bbuild -Werror=dev
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DPYBIND11_TEST_STABLE_ABI=ON
-DPYBIND11_ABI3T=ON
-DCMAKE_BUILD_TYPE=Release
-DPYTHON_EXECUTABLE="${{ steps.py315.outputs.python-path }}"

- name: Build
run: cmake --build build --config Release -j 4

- name: Python tests (3.15)
run: cmake --build build --config Release --target pytest

- name: Python tests (3.15t, same binaries)
working-directory: build/tests
run: >
"${{ steps.py315t.outputs.python-path }}" -m pytest -v ../../tests
${{ env.PYBIND11_STABLE_ABI_SKIPPED_TESTS }}

standard-large:
if: github.event.pull_request.draft == false
strategy:
Expand Down
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION
"To enforce that a handle_type_name<> specialization exists" OFF)
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
"Use simpler GIL management logic that does not support disassociation" OFF)
set(PYBIND11_TYPE_CREATION_VIA_SPEC
""
CACHE
STRING
"Create type objects with PyType_FromMetaclass: ON, OFF, or empty for the default (ON on CPython 3.12+)"
)
set(PYBIND11_INTERNALS_VERSION
""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
Expand Down Expand Up @@ -323,6 +329,11 @@ if(NOT TARGET pybind11_headers)
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
target_compile_definitions(pybind11_headers INTERFACE PYBIND11_SIMPLE_GIL_MANAGEMENT)
endif()
if(PYBIND11_TYPE_CREATION_VIA_SPEC)
target_compile_definitions(pybind11_headers INTERFACE PYBIND11_TYPE_CREATION_VIA_SPEC=1)
elseif(NOT "${PYBIND11_TYPE_CREATION_VIA_SPEC}" STREQUAL "")
target_compile_definitions(pybind11_headers INTERFACE PYBIND11_TYPE_CREATION_VIA_SPEC=0)
endif()
else()
# It is invalid to install a target twice, too.
set(PYBIND11_INSTALL OFF)
Expand Down
Loading
Loading