Files
pybind11/AGENTS.md
Henry SchreinerandRalf W. Grosse-Kunstleve 63d627c574 feat!: drop support for Python 3.8, MSVC 2017 (#6110)
* feat!: drop support for Python 3.8

The minimum supported version is now Python 3.9. pybind11 v3.0 was the last
release that supports Python 3.8. The deprecation note said that support goes
away in 3.1.

Remove the code paths that this makes dead:

- the `_PyObject_Vectorcall` fallback in `cast.h`
- the `frame->f_code` and `frame->f_back` fallbacks in `pytypes.h`
- the `PyFrame_FastToLocals` path in `get_type_override`
- the conditional `Py_VISIT(Py_TYPE(self))` in `tp_traverse`
- `PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX`, the pre-PyConfig interpreter
  init, and the `widen_chars` helpers in `embed.h`

Assisted-by: ClaudeCode:claude-opus-5

* ci(appveyor): use Python 3.9

The AppVeyor job set `PYTHON: 38`, which makes the path `C:\Python38`.
The image gives Python 3.9.13 as `C:\Python39`.

Assisted-by: ClaudeCode:claude-opus-5

* feat!: require MSVC 2019 or newer

Python 3.9 is the new minimum, so MSVC 2017 is no longer needed. Raise the
compile-time floor to _MSC_VER 1920 and remove the workarounds that only
applied below it: std::launder, fold expressions, weak_from_this, aligned
new/delete, the C4100 warning helper, and the func_handle syntax error.

AppVeyor now builds with Visual Studio 2019.

Assisted-by: ClaudeCode:claude-opus-5

* fix(appveyor): build against the Python that has the test packages

CMake 4 has no FindPythonLibs, so pybind11 uses FindPython. FindPython reads
the registry before PATH and selected `C:\Python314-x64`, but the test packages
go into the Python on PATH. Pass `Python_ROOT_DIR` to name the correct one.

Also set `CMAKE_ARCH`. It was never set, so the architecture came from the
generator. Visual Studio 2017 defaults to Win32, but Visual Studio 2019
defaults to x64, which made this x86 job build 64-bit code.

Assisted-by: ClaudeCode:claude-opus-5

* fix(appveyor): give the linker Python's libs directory

The build compiled but failed to link with LNK1104 on a bare `python39.lib`.
That name comes from the `#pragma comment(lib, ...)` in pyconfig.h, so the
linker needs the directory. CMake 4 has no FindPythonLibs, and FindPython does
not add it for this Debug x86 build. Put it on LIB instead.

The directory listing is temporary, to confirm the library is present.

Assisted-by: ClaudeCode:claude-opus-5

* fix(appveyor): link the release Python library in Debug

The image ships python39_d.lib next to python39.lib, so FindPython picks the
debug import library for a Debug build. pybind11 undefines _DEBUG around
Python.h, so pyconfig.h asks for python39.lib instead and the link failed with
LNK1104. Name the release library for the debug slot.

Setting LIB does not work, because MSBuild replaces it from the toolset, and it
would link both import libraries.

Assisted-by: ClaudeCode:claude-opus-5

* chore(appveyor): print link settings to debug LNK1104

Revert the two attempted fixes. Neither changed the failure: setting LIB does
not survive MSBuild, and naming the release library for Python_LIBRARY_DEBUG
had no effect.

Print the Python cache entries and the link settings of a generated project
file instead, to see what the linker really gets. Temporary.

Assisted-by: ClaudeCode:claude-opus-5

* fix(appveyor): take the release Python library in Debug

The generated project file linked C:\Python39\libs\python39_d.lib in the Debug
configuration, because the image ships debug binaries next to the release ones.
pybind11 undefines _DEBUG around Python.h, so pyconfig.h asks for python39.lib
in a #pragma comment(lib), which nothing on the link line satisfies and no
library directory holds. Map Debug to the release artifacts.

Assisted-by: ClaudeCode:claude-opus-5

* chore(appveyor): drop the temporary link diagnostic

Assisted-by: ClaudeCode:claude-opus-5

* fix(tests): guard the unraisable warning filter for pytest < 6

The distro pytest in the Clang and GCC Docker jobs has no
PytestUnraisableExceptionWarning, so an unconditional filterwarnings
mark makes pytest fail with an INTERNALERROR after the tests pass.

Assisted-by: ClaudeCode:claude-opus-5

Claude-Session: https://claude.ai/code/session_01Aimf6HuSz1vLRwBnbxmCTc

* docs: address review items on version hints, embed docs, and a PyPy xfail

Extend Python_ADDITIONAL_VERSIONS through 3.15, describe the PyConfig
behavior of initialize_interpreter, and drop the stale Python 3.8 wording
from the PyPy xfail reason.

Assisted-by: ClaudeCode:claude-opus-5

* Update README.rst

Co-authored-by: Ralf W. Grosse-Kunstleve <rwgkio@gmail.com>

---------

Co-authored-by: Ralf W. Grosse-Kunstleve <rwgkio@gmail.com>
2026-07-31 22:04:32 -07:00

6.6 KiB

Agent instructions

What this is

pybind11 v3 is a header-only C++ library that exposes C++ types to Python and vice versa. The "library" is entirely the headers under include/pybind11/; there is nothing to compile or link for a consumer. Everything else in the repo — the Python package, CMake tooling, and tests — exists to package, distribute, and verify those headers.

The version of record lives in include/pybind11/detail/common.h (as PYBIND11_VERSION_* macros); pybind11/_version.py parses it from there.

Build & test

The C++ tests need a compiler; the recommended flow uses CMake presets + uv:

cmake --workflow venv          # set up a .venv and run all tests (config + build + check)

Break it apart to control Python version or target:

cmake --preset venv -DPYBIND11_CREATE_WITH_UV=3.13t   # configure (e.g. free-threaded 3.13)
cmake --build --preset venv                           # build the test extension modules
cmake --build --preset venv -t cpptest                # build/run just the C++ (Catch2) tests

The default preset uses an existing Python/venv rather than creating one. Presets default to Ninja, Debug, PYBIND11_WERROR=ON, DOWNLOAD_CATCH=ON, DOWNLOAD_EIGEN=ON, and export compile_commands.json.

Manual full setup (no presets):

cmake -S . -B build -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON
cmake --build build -j4

Test targets

Run via cmake --build build --target <name>:

  • check — everything (pytest + cpptest + cmake build tests)
  • pytest — Python tests only
  • cpptest — C++ Catch2 tests only
  • test_cmake_build — install / add_subdirectory integration tests

Tests come in pairs: tests/test_<name>.cpp (binds C++ test fixtures into a module) and tests/test_<name>.py (exercises them with pytest). To build only a subset, configure with -DPYBIND11_TEST_OVERRIDE="test_callbacks;test_pickling" (names without extension; empty = all).

Run a single Python test directly (the .so is not installed into the venv, so run from the build tree's tests/ dir):

cd build && source .venv/bin/activate && cd tests
python -m pytest test_callbacks.py -k some_test

Pass pytest flags via PYTEST_ADDOPTS, e.g. env PYTEST_ADDOPTS="-s -x" cmake --build build --target pytest.

nox shortcuts (minimal setup, slower)

nox -s lint, nox -s tests-3.9, nox -s docs -- serve, nox -s build. With uvx nox you don't even need nox installed.

Linting

Use prek -a --quiet (Rust version of pre-commit run -a) before committing. Pre-commit handles all formatting and most linting.

clang-tidy support is built into CMake (cmake --preset tidy), normally run in CI/Docker, not locally.

Architecture of the headers

include/pybind11/pybind11.h is the main entry point that most users include; it pulls in the core machinery. Key layers:

  • detail/ — internal implementation, not part of the public API. This is where the hard parts live:
    • type_caster_base.h, cast.h (include/pybind11/cast.h) — the type conversion system that maps C++ ↔ Python values. Most binding behavior ultimately routes through type casters.
    • internals.h — the per-interpreter global state (registered types, instances, etc.), shared across modules via a capsule. ABI compatibility is gated by an ID; changing internals layout is an ABI break.
    • struct_smart_holder.h / using_smart_holder.hsmart_holder was added in v3, enabling safe passing of objects between shared_ptr/unique_ptr and Python ownership. It is the recommended holder for most situations, but for backward compatibility it is not the default holder, and there are no plans to make it the default holder in the future.
    • class.h, init.h — class registration and constructor (py::init) machinery.
  • pytypes.h — C++ wrappers for Python objects (py::object, py::dict, py::str, …) with reference counting.
  • Feature headers — opt-in includes: stl.h / stl/ / stl_bind.h (STL conversions), numpy.h + eigen/ (array/matrix interop), functional.h (std::function), chrono.h, complex.h, eval.h, embed.h (embedding the interpreter), iostream.h, gil.h / gil_safe_call_once.h, subinterpreter.h, native_enum.h, typing.h, warnings.h.
  • conduit/pybind11_conduit_v1, a stable cross-binding-framework protocol for sharing C++ objects between independently-built extension modules (even different pybind11 versions / other frameworks).

Because consumers can mix modules built against different pybind11 versions in one process, ABI stability is a first-class concern — be deliberate about anything touching internals.h, holders, or the platform ABI id (conduit/pybind11_platform_abi_id.h).

The Python package (pybind11/)

Separate from the C++ library: it ships the headers and CMake config so downstream projects can build extensions. Notable modules:

  • setup_helpers.pyPybind11Extension / build_ext and the intree_extensions helper for setuptools-based builds; this file is intentionally standalone-copyable.
  • commands.py / __main__.pypython -m pybind11 --includes / --cmakedir etc., used by build systems to locate headers and CMake files.

Packaging produces two distributions: the normal pybind11 (headers live inside the package, found via the functions above) and pybind11-global (installs to <env>/include/pybind11 and <env>/share/cmake/pybind11 for system-wide CMake discovery). Build with nox -s build and nox -s build_global. The build backend is scikit-build-core. Packaging tests live in tests/extra_python_package (nox -s tests_packaging).

CMake tooling (tools/)

pybind11Common.cmake, pybind11Tools.cmake (classic FindPythonLibs path), and pybind11NewTools.cmake (CMake 3.12+ FindPython path, selected with -DPYBIND11_FINDPYTHON=ON) implement the pybind11_add_module() interface and the pybind11::* interface targets that downstream CMakeLists.txt files consume.

Conventions

  • Small, self-contained PRs; this project deliberately favors minimal-code general solutions.
  • Any new functionality needs a test (add to or create the paired .cpp/.py in tests/, and register the test in tests/CMakeLists.txt).
  • Bug fixes should be paired with new tests that fail without the fix.
  • Try to add to an existing test file if possible (more files slow down tests)
  • Default C++ standard follows the consumer's toolchain; CI exercises a wide matrix (CPython 3.9+, PyPy, GraalPy; multiple compilers and C++ standards). Keep changes portable across that matrix.
  • PR descriptions follow a template, the key part is under "Suggested changelog entry", which is how we generate our changelog (with nox -s make_changelog)