Merge branch 'master' into stable

This commit is contained in:
Henry Schreiner 2024-03-27 19:52:11 -04:00
commit 01ab935612
91 changed files with 2326 additions and 565 deletions

View File

@ -135,7 +135,7 @@ The valid options are:
* Use `-G` and the name of a generator to use something different. `cmake
--help` lists the generators available.
- On Unix, setting `CMAKE_GENERATER=Ninja` in your environment will give
you automatic mulithreading on all your CMake projects!
you automatic multithreading on all your CMake projects!
* Open the `CMakeLists.txt` with QtCreator to generate for that IDE.
* You can use `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate the `.json` file
that some tools expect.

View File

@ -4,4 +4,12 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
groups:
actions:
patterns:
- "*"
ignore:
- dependency-name: actions/checkout
versions:
- "<5"

View File

@ -69,13 +69,12 @@ jobs:
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
allow-prereleases: true
- name: Setup Boost (Linux)
# Can't use boost + define _
@ -87,11 +86,11 @@ jobs:
run: brew install boost
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Cache wheels
if: runner.os == 'macOS'
uses: actions/cache@v3
uses: actions/cache@v4
with:
# This path is specific to macOS - we really only need it for PyPy NumPy wheels
# See https://github.com/actions/cache/blob/master/examples.md#python---pip
@ -109,12 +108,15 @@ jobs:
run: python -m pip install pytest-github-actions-annotate-failures
# First build - C++11 mode and inplace
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here.
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here
# (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime).
- name: Configure C++11 ${{ matrix.args }}
run: >
cmake -S . -B .
-DPYBIND11_WERROR=ON
-DPYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION=ON
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON
-DPYBIND11_NUMPY_1_ONLY=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=11
@ -139,11 +141,13 @@ jobs:
# Second build - C++17 mode and in a build directory
# More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here.
# (same for PYBIND11_NUMPY_1_ONLY, but requires a NumPy 1.x at runtime).
- name: Configure C++17
run: >
cmake -S . -B build2
-DPYBIND11_WERROR=ON
-DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF
-DPYBIND11_NUMPY_1_ONLY=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=17
@ -195,9 +199,10 @@ jobs:
matrix:
include:
# TODO: Fails on 3.10, investigate
- python-version: "3.9"
python-debug: true
valgrind: true
# JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4889
# - python-version: "3.9"
# python-debug: true
# valgrind: true
- python-version: "3.11"
python-debug: false
@ -205,20 +210,20 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }} (deadsnakes)
uses: deadsnakes/action@v3.0.1
uses: deadsnakes/action@v3.1.0
with:
python-version: ${{ matrix.python-version }}
debug: ${{ matrix.python-debug }}
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Valgrind cache
if: matrix.valgrind
uses: actions/cache@v3
uses: actions/cache@v4
id: cache-valgrind
with:
path: valgrind
@ -302,12 +307,15 @@ jobs:
- clang: 15
std: 20
container_suffix: "-bullseye"
- clang: 16
std: 20
container_suffix: "-bullseye"
name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64"
container: "silkeh/clang:${{ matrix.clang }}${{ matrix.container_suffix }}"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add wget and python3
run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev
@ -341,7 +349,7 @@ jobs:
container: nvidia/cuda:12.2.0-devel-ubuntu22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
# tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
- name: Install 🐍 3
@ -365,7 +373,7 @@ jobs:
# container: centos:8
#
# steps:
# - uses: actions/checkout@v3
# - uses: actions/checkout@v4
#
# - name: Add Python 3 and a few requirements
# run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules
@ -401,54 +409,55 @@ jobs:
# run: cmake --build build --target test_cmake_build
# Testing on CentOS 7 + PGI compilers, which seems to require more workarounds
centos-nvhpc7:
if: ${{ false }} # JOB DISABLED (NEEDS WORK): https://github.com/pybind/pybind11/issues/4690
runs-on: ubuntu-latest
name: "🐍 3 • CentOS7 / PGI 22.9 • x64"
container: centos:7
# Testing on Ubuntu + NVHPC (previous PGI) compilers, which seems to require more workarounds
ubuntu-nvhpc7:
runs-on: ubuntu-20.04
name: "🐍 3 • NVHPC 23.5 • C++17 • x64"
env:
# tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
DEBIAN_FRONTEND: 'noninteractive'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add Python 3 and a few requirements
run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 yum-utils
- name: Add NVHPC Repo
run: |
echo 'deb [trusted=yes] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | \
sudo tee /etc/apt/sources.list.d/nvhpc.list
- name: Install NVidia HPC SDK
run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.9
- name: Install 🐍 3 & NVHPC
run: |
sudo apt-get update -y && \
sudo apt-get install -y cmake environment-modules git python3-dev python3-pip python3-numpy && \
sudo apt-get install -y --no-install-recommends nvhpc-23-5 && \
sudo rm -rf /var/lib/apt/lists/*
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pytest
# On CentOS 7, we have to filter a few tests (compiler internal error)
# and allow deeper template recursion (not needed on CentOS 8 with a newer
# standard library). On some systems, you many need further workarounds:
# On some systems, you many need further workarounds:
# https://github.com/pybind/pybind11/pull/2475
- name: Configure
shell: bash
run: |
source /etc/profile.d/modules.sh
module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.9
cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \
-DCMAKE_CXX_STANDARD=11 \
module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/23.5
cmake -S . -B build -DDOWNLOAD_CATCH=ON \
-DCMAKE_CXX_STANDARD=17 \
-DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \
-DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0" \
-DPYBIND11_TEST_FILTER="test_smart_ptr.cpp"
# Building before installing Pip should produce a warning but not an error
- name: Build
run: cmake3 --build build -j 2 --verbose
- name: Install CMake with pip
run: |
python3 -m pip install --upgrade pip
python3 -m pip install pytest
run: cmake --build build -j 2 --verbose
- name: Python tests
run: cmake3 --build build --target pytest
run: cmake --build build --target pytest
- name: C++ tests
run: cmake3 --build build --target cpptest
run: cmake --build build --target cpptest
- name: Interface test
run: cmake3 --build build --target test_cmake_build
run: cmake --build build --target test_cmake_build
# Testing on GCC using the GCC docker images (only recent images supported)
@ -465,12 +474,13 @@ jobs:
- { gcc: 10, std: 17 }
- { gcc: 11, std: 20 }
- { gcc: 12, std: 20 }
- { gcc: 13, std: 20 }
name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64"
container: "gcc:${{ matrix.gcc }}"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add Python 3
run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev
@ -479,7 +489,7 @@ jobs:
run: python3 -m pip install --upgrade pip
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Configure
shell: bash
@ -524,13 +534,11 @@ jobs:
# Testing on ICC using the oneAPI apt repo
icc:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
name: "🐍 3 • ICC latest • x64"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Add apt repo
run: |
@ -634,7 +642,13 @@ jobs:
container: "${{ matrix.container }}"
steps:
- uses: actions/checkout@v3
- name: Latest actions/checkout
uses: actions/checkout@v4
if: matrix.container != 'centos:7'
- name: Pin actions/checkout as required for centos:7
uses: actions/checkout@v3
if: matrix.container == 'centos:7'
- name: Add Python 3 (RHEL 7)
if: matrix.container == 'centos:7'
@ -651,6 +665,11 @@ jobs:
run: |
python3 -m pip install cmake -r tests/requirements.txt
- name: Ensure NumPy 2 is used (required Python >= 3.9)
if: matrix.container == 'almalinux:9'
run: |
python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1'
- name: Configure
shell: bash
run: >
@ -682,7 +701,7 @@ jobs:
container: i386/debian:buster
steps:
- uses: actions/checkout@v1 # Required to run inside docker
- uses: actions/checkout@v1 # v1 is required to run inside docker
- name: Install requirements
run: |
@ -725,9 +744,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
@ -777,19 +796,19 @@ jobs:
runs-on: windows-2019
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
architecture: x86
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.12.1
uses: ilammy/msvc-dev-cmd@v1.13.0
with:
arch: x86
@ -830,19 +849,19 @@ jobs:
runs-on: windows-2019
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
architecture: x86
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Prepare MSVC
uses: ilammy/msvc-dev-cmd@v1.12.1
uses: ilammy/msvc-dev-cmd@v1.13.0
with:
arch: x86
@ -878,19 +897,21 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Prepare env
# Ensure use of NumPy 2 (via NumPy nightlies but can be changed soon)
run: |
python3 -m pip install -r tests/requirements.txt
python3 -m pip install 'numpy>=2.0.0b1' 'scipy>=1.13.0rc1'
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Configure C++20
run: >
@ -948,7 +969,6 @@ jobs:
mingw-w64-${{matrix.env}}-gcc
mingw-w64-${{matrix.env}}-python-pip
mingw-w64-${{matrix.env}}-python-numpy
mingw-w64-${{matrix.env}}-python-scipy
mingw-w64-${{matrix.env}}-cmake
mingw-w64-${{matrix.env}}-make
mingw-w64-${{matrix.env}}-python-pytest
@ -956,12 +976,23 @@ jobs:
mingw-w64-${{matrix.env}}-boost
mingw-w64-${{matrix.env}}-catch
- uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2
if: matrix.sys == 'mingw64'
with:
msystem: ${{matrix.sys}}
install: >-
git
mingw-w64-${{matrix.env}}-python-scipy
- uses: actions/checkout@v4
- name: Configure C++11
# LTO leads to many undefined reference like
# `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&)
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build
run: >-
cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
-DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
-S . -B build
- name: Build C++11
run: cmake --build build -j 2
@ -979,7 +1010,10 @@ jobs:
run: git clean -fdx
- name: Configure C++14
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build2
run: >-
cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
-DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
-S . -B build2
- name: Build C++14
run: cmake --build build2 -j 2
@ -997,7 +1031,10 @@ jobs:
run: git clean -fdx
- name: Configure C++17
run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build3
run: >-
cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
-DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)")
-S . -B build3
- name: Build C++17
run: cmake --build build3 -j 2
@ -1027,21 +1064,21 @@ jobs:
run: env
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Clang
uses: egor-tensin/setup-clang@v1
- name: Setup Python ${{ matrix.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Install ninja-build tool
uses: seanmiddleditch/gha-setup-ninja@v3
uses: seanmiddleditch/gha-setup-ninja@v4
- name: Run pip installs
run: |
@ -1096,7 +1133,7 @@ jobs:
run: env
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Show Clang++ version before brew install llvm
run: clang++ --version
@ -1108,7 +1145,7 @@ jobs:
run: clang++ --version
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Run pip installs
run: |

View File

@ -49,10 +49,10 @@ jobs:
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python 3.7
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.7
architecture: ${{ matrix.arch }}
@ -63,7 +63,7 @@ jobs:
# An action for adding a specific version of CMake:
# https://github.com/jwlawson/actions-setup-cmake
- name: Setup CMake ${{ matrix.cmake }}
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
with:
cmake-version: ${{ matrix.cmake }}

View File

@ -25,13 +25,13 @@ jobs:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Add matchers
run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
- uses: pre-commit/action@v3.0.0
- uses: pre-commit/action@v3.0.1
with:
# Slow hooks are marked with manual - slow is okay here, run them too
extra_args: --hook-stage manual --all-files
@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
container: silkeh/clang:15-bullseye
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install requirements
run: apt-get update && apt-get install -y git python3-dev python3-pytest

View File

@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/labeler@main
- uses: actions/labeler@v4
if: >
github.event.pull_request.merged == true &&
!startsWith(github.event.pull_request.title, 'chore(deps):') &&

View File

@ -28,10 +28,10 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup 🐍 3.6
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.6
@ -50,10 +50,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup 🐍 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
@ -73,13 +73,13 @@ jobs:
run: twine check dist/*
- name: Save standard package
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: standard
path: dist/pybind11-*
- name: Save global package
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: global
path: dist/pybind11_global-*
@ -94,12 +94,12 @@ jobs:
needs: [packaging]
steps:
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
# Downloads all to directories matching the artifact names
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
- name: Publish standard package
uses: pypa/gh-action-pypi-publish@release/v1

View File

@ -13,30 +13,30 @@ concurrency:
env:
PIP_BREAK_SYSTEM_PACKAGES: 1
PIP_ONLY_BINARY: ":all:"
# For cmake:
VERBOSE: 1
jobs:
standard:
name: "🐍 3.12 latest • ubuntu-latest • x64"
name: "🐍 3.13 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')"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v4
- name: Setup Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.12-dev"
python-version: "3.13"
allow-prereleases: true
- name: Setup Boost
run: sudo apt-get install libboost-dev
- name: Update CMake
uses: jwlawson/actions-setup-cmake@v1.14
uses: jwlawson/actions-setup-cmake@v2.0
- name: Run pip installs
run: |

View File

@ -25,27 +25,22 @@ repos:
# Clang format the codebase automatically
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v16.0.6"
rev: "v17.0.6"
hooks:
- id: clang-format
types_or: [c++, c, cuda]
# Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black
rev: "23.3.0" # Keep in sync with blacken-docs
hooks:
- id: black
# Ruff, the Python auto-correcting linter written in Rust
# Ruff, the Python auto-correcting linter/formatter written in Rust
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.276
rev: v0.2.0
hooks:
- id: ruff
args: ["--fix", "--show-fixes"]
- id: ruff-format
# Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.4.1"
rev: "v1.8.0"
hooks:
- id: mypy
args: []
@ -67,7 +62,7 @@ repos:
# Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v4.4.0"
rev: "v4.5.0"
hooks:
- id: check-added-large-files
- id: check-case-conflict
@ -84,21 +79,21 @@ repos:
# Also code format the docs
- repo: https://github.com/asottile/blacken-docs
rev: "1.14.0"
rev: "1.16.0"
hooks:
- id: blacken-docs
additional_dependencies:
- black==23.3.0 # keep in sync with black hook
- black==23.*
# Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: "v1.5.1"
rev: "v1.5.4"
hooks:
- id: remove-tabs
# Avoid directional quotes
- repo: https://github.com/sirosen/texthooks
rev: "0.5.0"
rev: "0.6.4"
hooks:
- id: fix-ligatures
- id: fix-smartquotes
@ -124,7 +119,7 @@ repos:
# Use tools/codespell_ignore_lines_from_errors.py
# to rebuild .codespell-ignore-lines
- repo: https://github.com/codespell-project/codespell
rev: "v2.2.5"
rev: "v2.2.6"
hooks:
- id: codespell
exclude: ".supp$"
@ -132,7 +127,7 @@ repos:
# Check for common shell mistakes
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: "v0.9.0.5"
rev: "v0.9.0.6"
hooks:
- id: shellcheck
@ -142,12 +137,19 @@ repos:
- id: disallow-caps
name: Disallow improper capitalization
language: pygrep
entry: PyBind|Numpy|Cmake|CCache|PyTest
entry: PyBind|\bNumpy\b|Cmake|CCache|PyTest
exclude: ^\.pre-commit-config.yaml$
# PyLint has native support - not always usable, but works for us
- repo: https://github.com/PyCQA/pylint
rev: "v3.0.0a6"
rev: "v3.0.3"
hooks:
- id: pylint
files: ^pybind11
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.28.0
hooks:
- id: check-readthedocs
- id: check-github-workflows
- id: check-dependabot

View File

@ -1,3 +1,20 @@
# https://blog.readthedocs.com/migrate-configuration-v2/
version: 2
build:
os: ubuntu-22.04
apt_packages:
- librsvg2-bin
tools:
python: "3.11"
sphinx:
configuration: docs/conf.py
python:
version: 3
requirements_file: docs/requirements.txt
install:
- requirements: docs/requirements.txt
formats:
- pdf

View File

@ -5,15 +5,25 @@
# All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
# Propagate this policy (FindPythonInterp removal) so it can be detected later
if(NOT CMAKE_VERSION VERSION_LESS "3.27")
cmake_policy(GET CMP0148 _pybind11_cmp0148)
endif()
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
if(_pybind11_cmp0148)
cmake_policy(SET CMP0148 ${_pybind11_cmp0148})
unset(_pybind11_cmp0148)
endif()
# Avoid infinite recursion if tests include this as a subdirectory
@ -82,33 +92,58 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(pybind11_system "")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(CMAKE_VERSION VERSION_LESS "3.18")
set(_pybind11_findpython_default OFF)
else()
set(_pybind11_findpython_default ON)
endif()
else()
set(PYBIND11_MASTER_PROJECT OFF)
set(pybind11_system SYSTEM)
set(_pybind11_findpython_default OFF)
endif()
# Options
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
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)
option(PYBIND11_NUMPY_1_ONLY
"Disable NumPy 2 support to avoid changes to previous pybind11 versions." OFF)
set(PYBIND11_INTERNALS_VERSION
""
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
if(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
add_compile_definitions(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION)
endif()
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
endif()
if(PYBIND11_NUMPY_1_ONLY)
add_compile_definitions(PYBIND11_NUMPY_1_ONLY)
endif()
cmake_dependent_option(
USE_PYTHON_INCLUDE_DIR
"Install pybind11 headers in Python include directory instead of default installation prefix"
OFF "PYBIND11_INSTALL" OFF)
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" OFF
cmake_dependent_option(PYBIND11_FINDPYTHON "Force new FindPython" ${_pybind11_findpython_default}
"NOT CMAKE_VERSION VERSION_LESS 3.12" OFF)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes).
if(PYBIND11_MASTER_PROJECT
AND PYBIND11_FINDPYTHON
AND DEFINED PYTHON_EXECUTABLE
AND NOT DEFINED Python_EXECUTABLE)
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif()
# NB: when adding a header don't forget to also add it to setup.py
set(PYBIND11_HEADERS
include/pybind11/detail/class.h
@ -132,6 +167,7 @@ set(PYBIND11_HEADERS
include/pybind11/embed.h
include/pybind11/eval.h
include/pybind11/gil.h
include/pybind11/gil_safe_call_once.h
include/pybind11/iostream.h
include/pybind11/functional.h
include/pybind11/numpy.h
@ -141,7 +177,8 @@ set(PYBIND11_HEADERS
include/pybind11/stl.h
include/pybind11/stl_bind.h
include/pybind11/stl/filesystem.h
include/pybind11/type_caster_pyobject_ptr.h)
include/pybind11/type_caster_pyobject_ptr.h
include/pybind11/typing.h)
# Compare with grep and warn if mismatched
if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12)
@ -277,7 +314,21 @@ if(PYBIND11_INSTALL)
# pkg-config support
if(NOT prefix_for_pc_file)
if(IS_ABSOLUTE "${CMAKE_INSTALL_DATAROOTDIR}")
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}")
else()
set(pc_datarootdir "${CMAKE_INSTALL_DATAROOTDIR}")
if(CMAKE_VERSION VERSION_LESS 3.20)
set(prefix_for_pc_file "\${pcfiledir}/..")
while(pc_datarootdir)
get_filename_component(pc_datarootdir "${pc_datarootdir}" DIRECTORY)
string(APPEND prefix_for_pc_file "/..")
endwhile()
else()
cmake_path(RELATIVE_PATH CMAKE_INSTALL_PREFIX BASE_DIRECTORY CMAKE_INSTALL_DATAROOTDIR
OUTPUT_VARIABLE prefix_for_pc_file)
endif()
endif()
endif()
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in"

View File

@ -36,10 +36,10 @@ with everything stripped away that isn't relevant for binding
generation. Without comments, the core header files only require ~4K
lines of code and depend on Python (3.6+, or PyPy) and the C++
standard library. This compact implementation was possible thanks to
some of the new C++11 language features (specifically: tuples, lambda
functions and variadic templates). Since its creation, this library has
grown beyond Boost.Python in many ways, leading to dramatically simpler
binding code in many common situations.
some C++11 language features (specifically: tuples, lambda functions and
variadic templates). Since its creation, this library has grown beyond
Boost.Python in many ways, leading to dramatically simpler binding code in many
common situations.
Tutorial and reference documentation is provided at
`pybind11.readthedocs.io <https://pybind11.readthedocs.io/en/latest>`_.
@ -71,6 +71,7 @@ pybind11 can map the following core C++ features to Python:
- Internal references with correct reference counting
- C++ classes with virtual (and pure virtual) methods can be extended
in Python
- Integrated NumPy support (NumPy 2 requires pybind11 2.12+)
Goodies
-------

View File

@ -18,7 +18,7 @@ information, see :doc:`/compiling`.
.. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.26)
cmake_minimum_required(VERSION 3.5...3.27)
project(example)
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`

View File

@ -127,8 +127,7 @@ before a global translator is tried.
Inside the translator, ``std::rethrow_exception`` should be used within
a try block to re-throw the exception. One or more catch clauses to catch
the appropriate exceptions should then be used with each clause using
``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set
the python exception to a custom exception type (see below).
``py::set_error()`` (see below).
To declare a custom Python exception type, declare a ``py::exception`` variable
and use this in the associated exception translator (note: it is often useful
@ -142,14 +141,16 @@ standard python RuntimeError:
.. code-block:: cpp
static py::exception<MyCustomException> exc(m, "MyCustomError");
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> exc_storage;
exc_storage.call_once_and_store_result(
[&]() { return py::exception<MyCustomException>(m, "MyCustomError"); });
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const MyCustomException &e) {
exc(e.what());
py::set_error(exc_storage.get_stored(), e.what());
} catch (const OtherException &e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
py::set_error(PyExc_RuntimeError, e.what());
}
});
@ -168,8 +169,7 @@ section.
.. note::
Call either ``PyErr_SetString`` or a custom exception's call
operator (``exc(string)``) for every exception caught in a custom exception
Call ``py::set_error()`` for every exception caught in a custom exception
translator. Failure to do so will cause Python to crash with ``SystemError:
error return without exception set``.
@ -200,7 +200,7 @@ If module1 has the following translator:
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
PyErr_SetString("module1 handled this")
py::set_error(PyExc_ArgumentError, "module1 handled this");
}
}
@ -212,7 +212,7 @@ and module2 has the following similar translator:
try {
if (p) std::rethrow_exception(p);
} catch (const std::invalid_argument &e) {
PyErr_SetString("module2 handled this")
py::set_error(PyExc_ArgumentError, "module2 handled this");
}
}
@ -312,11 +312,11 @@ error protocol, which is outlined here.
After calling the Python C API, if Python returns an error,
``throw py::error_already_set();``, which allows pybind11 to deal with the
exception and pass it back to the Python interpreter. This includes calls to
the error setting functions such as ``PyErr_SetString``.
the error setting functions such as ``py::set_error()``.
.. code-block:: cpp
PyErr_SetString(PyExc_TypeError, "C API type error demo");
py::set_error(PyExc_TypeError, "C API type error demo");
throw py::error_already_set();
// But it would be easier to simply...

View File

@ -16,7 +16,7 @@ lifetime of objects managed by them. This can lead to issues when creating
bindings for functions that return a non-trivial type. Just by looking at the
type information, it is not clear whether Python should take charge of the
returned value and eventually free its resources, or if this is handled on the
C++ side. For this reason, pybind11 provides a several *return value policy*
C++ side. For this reason, pybind11 provides several *return value policy*
annotations that can be passed to the :func:`module_::def` and
:func:`class_::def` functions. The default policy is
:enum:`return_value_policy::automatic`.

View File

@ -398,3 +398,32 @@ before they are used as a parameter or return type of a function:
pyFoo.def(py::init<const ns::Bar&>());
pyBar.def(py::init<const ns::Foo&>());
}
Setting inner type hints in docstrings
======================================
When you use pybind11 wrappers for ``list``, ``dict``, and other generic python
types, the docstring will just display the generic type. You can convey the
inner types in the docstring by using a special 'typed' version of the generic
type.
.. code-block:: cpp
PYBIND11_MODULE(example, m) {
m.def("pass_list_of_str", [](py::typing::List<py::str> arg) {
// arg can be used just like py::list
));
}
The resulting docstring will be ``pass_list_of_str(arg0: list[str]) -> None``.
The following special types are available in ``pybind11/typing.h``:
* ``py::Tuple<Args...>``
* ``py::Dict<K, V>``
* ``py::List<V>``
* ``py::Set<V>``
* ``py::Callable<Signature>``
.. warning:: Just like in python, these are merely hints. They don't actually
enforce the types of their contents at runtime or compile time.

View File

@ -70,7 +70,7 @@ def generate_dummy_code_boost(nclasses=10):
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
print("{")
for i in range(0, 10):
for i in range(10):
nclasses = 2**i
with open("test.cpp", "w") as f:
f.write(codegen(nclasses))

View File

@ -10,8 +10,188 @@ Changes will be added here periodically from the "Suggested changelog entry"
block in pull request descriptions.
IN DEVELOPMENT
--------------
Changes will be summarized here periodically.
Version 2.12.0 (March 27, 2025)
-------------------------------
New Features:
* ``pybind11`` now supports compiling for
`NumPy 2 <https://numpy.org/devdocs/numpy_2_0_migration_guide.html>`_. Most
code shouldn't change (see :ref:`upgrade-guide-2.12` for details). However,
if you experience issues you can define ``PYBIND11_NUMPY_1_ONLY`` to disable
the new support for now, but this will be removed in the future.
`#5050 <https://github.com/pybind/pybind11/pull/5050>`_
* ``pybind11/gil_safe_call_once.h`` was added (it needs to be included
explicitly). The primary use case is GIL-safe initialization of C++
``static`` variables.
`#4877 <https://github.com/pybind/pybind11/pull/4877>`_
* Support move-only iterators in ``py::make_iterator``,
``py::make_key_iterator``, ``py::make_value_iterator``.
`#4834 <https://github.com/pybind/pybind11/pull/4834>`_
* Two simple ``py::set_error()`` functions were added and the documentation was
updated accordingly. In particular, ``py::exception<>::operator()`` was
deprecated (use one of the new functions instead). The documentation for
``py::exception<>`` was further updated to not suggest code that may result
in undefined behavior.
`#4772 <https://github.com/pybind/pybind11/pull/4772>`_
Bug fixes:
* Removes potential for Undefined Behavior during process teardown.
`#4897 <https://github.com/pybind/pybind11/pull/4897>`_
* Improve compatibility with the nvcc compiler (especially CUDA 12.1/12.2).
`#4893 <https://github.com/pybind/pybind11/pull/4893>`_
* ``pybind11/numpy.h`` now imports NumPy's ``multiarray`` and ``_internal``
submodules with paths depending on the installed version of NumPy (for
compatibility with NumPy 2).
`#4857 <https://github.com/pybind/pybind11/pull/4857>`_
* Builtins collections names in docstrings are now consistently rendered in
lowercase (list, set, dict, tuple), in accordance with PEP 585.
`#4833 <https://github.com/pybind/pybind11/pull/4833>`_
* Added ``py::typing::Iterator<T>``, ``py::typing::Iterable<T>``.
`#4832 <https://github.com/pybind/pybind11/pull/4832>`_
* Render ``py::function`` as ``Callable`` in docstring.
`#4829 <https://github.com/pybind/pybind11/pull/4829>`_
* Also bump ``PYBIND11_INTERNALS_VERSION`` for MSVC, which unlocks two new
features without creating additional incompatibilities.
`#4819 <https://github.com/pybind/pybind11/pull/4819>`_
* Guard against crashes/corruptions caused by modules built with different MSVC
versions.
`#4779 <https://github.com/pybind/pybind11/pull/4779>`_
* A long-standing bug in the handling of Python multiple inheritance was fixed.
See PR #4762 for the rather complex details.
`#4762 <https://github.com/pybind/pybind11/pull/4762>`_
* Fix ``bind_map`` with ``using`` declarations.
`#4952 <https://github.com/pybind/pybind11/pull/4952>`_
* Qualify ``py::detail::concat`` usage to avoid ADL selecting one from
somewhere else, such as modernjson's concat.
`#4955 <https://github.com/pybind/pybind11/pull/4955>`_
* Use new PyCode API on Python 3.12+.
`#4916 <https://github.com/pybind/pybind11/pull/4916>`_
* Minor cleanup from warnings reported by Clazy.
`#4988 <https://github.com/pybind/pybind11/pull/4988>`_
* Remove typing and duplicate ``class_`` for ``KeysView``/``ValuesView``/``ItemsView``.
`#4985 <https://github.com/pybind/pybind11/pull/4985>`_
* Use ``PyObject_VisitManagedDict()`` and ``PyObject_ClearManagedDict()`` on Python 3.13 and newer.
`#4973 <https://github.com/pybind/pybind11/pull/4973>`_
* Update ``make_static_property_type()`` to make it compatible with Python 3.13.
`#4971 <https://github.com/pybind/pybind11/pull/4971>`_
.. fix(types)
* Render typed iterators for ``make_iterator``, ``make_key_iterator``,
``make_value_iterator``.
`#4876 <https://github.com/pybind/pybind11/pull/4876>`_
* Add several missing type name specializations.
`#5073 <https://github.com/pybind/pybind11/pull/5073>`_
* Change docstring render for ``py::buffer``, ``py::sequence`` and
``py::handle`` (to ``Buffer``, ``Sequence``, ``Any``).
`#4831 <https://github.com/pybind/pybind11/pull/4831>`_
* Fixed ``base_enum.__str__`` docstring.
`#4827 <https://github.com/pybind/pybind11/pull/4827>`_
* Enforce single line docstring signatures.
`#4735 <https://github.com/pybind/pybind11/pull/4735>`_
* Special 'typed' wrappers now available in ``typing.h`` to annotate tuple, dict,
list, set, and function.
`#4259 <https://github.com/pybind/pybind11/pull/4259>`_
* Create ``handle_type_name`` specialization to type-hint variable length tuples.
`#5051 <https://github.com/pybind/pybind11/pull/5051>`_
.. fix(build)
* Setting ``PYBIND11_FINDPYTHON`` to OFF will force the old FindPythonLibs mechanism to be used.
`#5042 <https://github.com/pybind/pybind11/pull/5042>`_
* Skip empty ``PYBIND11_PYTHON_EXECUTABLE_LAST`` for the first cmake run.
`#4856 <https://github.com/pybind/pybind11/pull/4856>`_
* Fix FindPython mode exports & avoid ``pkg_resources`` if
``importlib.metadata`` available.
`#4941 <https://github.com/pybind/pybind11/pull/4941>`_
* ``Python_ADDITIONAL_VERSIONS`` (classic search) now includes 3.12.
`#4909 <https://github.com/pybind/pybind11/pull/4909>`_
* ``pybind11.pc`` is now relocatable by default as long as install destinations
are not absolute paths.
`#4830 <https://github.com/pybind/pybind11/pull/4830>`_
* Correctly detect CMake FindPython removal when used as a subdirectory.
`#4806 <https://github.com/pybind/pybind11/pull/4806>`_
* Don't require the libs component on CMake 3.18+ when using
PYBIND11_FINDPYTHON (fixes manylinux builds).
`#4805 <https://github.com/pybind/pybind11/pull/4805>`_
* ``pybind11_strip`` is no longer automatically applied when
``CMAKE_BUILD_TYPE`` is unset.
`#4780 <https://github.com/pybind/pybind11/pull/4780>`_
* Support ``DEBUG_POSFIX`` correctly for debug builds.
`#4761 <https://github.com/pybind/pybind11/pull/4761>`_
* Hardcode lto/thin lto for Emscripten cross-compiles.
`#4642 <https://github.com/pybind/pybind11/pull/4642>`_
* Upgrade maximum supported CMake version to 3.27 to fix CMP0148 warnings.
`#4786 <https://github.com/pybind/pybind11/pull/4786>`_
Documentation:
* Small fix to grammar in ``functions.rst``.
`#4791 <https://github.com/pybind/pybind11/pull/4791>`_
* Remove upper bound in example pyproject.toml for setuptools.
`#4774 <https://github.com/pybind/pybind11/pull/4774>`_
CI:
* CI: Update NVHPC to 23.5 and Ubuntu 20.04.
`#4764 <https://github.com/pybind/pybind11/pull/4764>`_
* Test on PyPy 3.10.
`#4714 <https://github.com/pybind/pybind11/pull/4714>`_
Other:
* Use Ruff formatter instead of Black.
`#4912 <https://github.com/pybind/pybind11/pull/4912>`_
* An ``assert()`` was added to help Coverty avoid generating a false positive.
`#4817 <https://github.com/pybind/pybind11/pull/4817>`_
Version 2.11.1 (July 17, 2023)
-----------------------------
------------------------------
Changes:
@ -26,7 +206,7 @@ Changes:
Version 2.11.0 (July 14, 2023)
-----------------------------
------------------------------
New features:

View File

@ -143,7 +143,7 @@ Your ``pyproject.toml`` file will likely look something like this:
.. code-block:: toml
[build-system]
requires = ["setuptools>=42", "wheel", "pybind11~=2.6.1"]
requires = ["setuptools>=42", "pybind11>=2.6.1"]
build-backend = "setuptools.build_meta"
.. note::
@ -241,7 +241,7 @@ extension module can be created with just a few lines of code:
.. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.26)
cmake_minimum_required(VERSION 3.5...3.27)
project(example LANGUAGES CXX)
add_subdirectory(pybind11)
@ -498,7 +498,7 @@ You can use these targets to build complex applications. For example, the
.. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.26)
cmake_minimum_required(VERSION 3.5...3.27)
project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -556,7 +556,7 @@ information about usage in C++, see :doc:`/advanced/embedding`.
.. code-block:: cmake
cmake_minimum_required(VERSION 3.5...3.26)
cmake_minimum_required(VERSION 3.5...3.27)
project(example LANGUAGES CXX)
find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11)
@ -639,3 +639,11 @@ cross-project dependency management. Additionally, it is able to autogenerate
customizable pybind11-based wrappers by parsing C++ header files.
.. [robotpy-build] https://robotpy-build.readthedocs.io
[litgen]_ is an automatic python bindings generator with a focus on generating
documented and discoverable bindings: bindings will nicely reproduce the documentation
found in headers. It is is based on srcML (srcml.org), a highly scalable, multi-language
parsing tool with a developer centric approach. The API that you want to expose to python
must be C++14 compatible (but your implementation can use more modern constructs).
.. [litgen] https://pthom.github.io/litgen

View File

@ -15,8 +15,8 @@ For example:
For beta, ``PYBIND11_VERSION_PATCH`` should be ``Z.b1``. RC's can be ``Z.rc1``.
Always include the dot (even though PEP 440 allows it to be dropped). For a
final release, this must be a simple integer. There is also a HEX version of
the version just below.
final release, this must be a simple integer. There is also
``PYBIND11_VERSION_HEX`` just below that needs to be updated.
To release a new version of pybind11:
@ -26,52 +26,92 @@ If you don't have nox, you should either use ``pipx run nox`` instead, or use
``pipx install nox`` or ``brew install nox`` (Unix).
- Update the version number
- Update ``PYBIND11_VERSION_MAJOR`` etc. in
``include/pybind11/detail/common.h``. PATCH should be a simple integer.
- Update the version HEX just below, as well.
- Update ``pybind11/_version.py`` (match above)
- Update ``PYBIND11_VERSION_HEX`` just below as well.
- Update ``pybind11/_version.py`` (match above).
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
- Ensure that all the information in ``setup.cfg`` is up-to-date, like
supported Python versions.
- Add release date in ``docs/changelog.rst`` and integrate the output of
``nox -s make_changelog``.
- Note that the ``make_changelog`` command inspects
- Note that the ``nox -s make_changelog`` command inspects
`needs changelog <https://github.com/pybind/pybind11/pulls?q=is%3Apr+is%3Aclosed+label%3A%22needs+changelog%22>`_.
- Manually clear the ``needs changelog`` labels using the GitHub web
interface (very easy: start by clicking the link above).
- ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it
fails due to a known flake issue, either ignore or restart CI.)
- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version
- Add a release branch if this is a new MINOR version, or update the existing
release branch if it is a patch version
- New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y``
- Update branch: ``git checkout vX.Y``, ``git merge <release branch>``, ``git push``
- Update tags (optional; if you skip this, the GitHub release makes a
non-annotated tag for you)
- ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``.
- ``git push --tags``.
- ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``
- ``grep ^__version__ pybind11/_version.py``
- Last-minute consistency check: same as tag?
- ``git push --tags``
- Update stable
- ``git checkout stable``
- ``git merge master``
- ``git merge -X theirs vX.Y.Z``
- ``git diff vX.Y.Z``
- Carefully review and reconcile any diffs. There should be none.
- ``git push``
- Make a GitHub release (this shows up in the UI, sends new release
notifications to users watching releases, and also uploads PyPI packages).
(Note: if you do not use an existing tag, this creates a new lightweight tag
for you, so you could skip the above step.)
- GUI method: Under `releases <https://github.com/pybind/pybind11/releases>`_
click "Draft a new release" on the far right, fill in the tag name
(if you didn't tag above, it will be made here), fill in a release name
like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog
into the description (usually ``cat docs/changelog.rst | pandoc -f rst -t gfm``).
into the description. You can use ``cat docs/changelog.rst | pandoc -f rst -t gfm``,
then manually remove line breaks and strip links to PRs and issues,
e.g. to a bare ``#1234``, without the surrounding ``<...>_`` hyperlink markup.
Check "pre-release" if this is a beta/RC.
- CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"``
If this is a pre-release, add ``-p``.
- Get back to work
- Make sure you are on master, not somewhere else: ``git checkout master``
- Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to
``0.dev1`` and increment MINOR).
- Update ``_version.py`` to match
- Update ``pybind11/_version.py`` to match.
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
- Add a spot for in-development updates in ``docs/changelog.rst``.
- If the release was a new MINOR version, add a new ``IN DEVELOPMENT``
section in ``docs/changelog.rst``.
- ``git add``, ``git commit``, ``git push``
If a version branch is updated, remember to set PATCH to ``1.dev1``.
@ -89,7 +129,11 @@ merge it if there are no issues.
Manual packaging
^^^^^^^^^^^^^^^^
If you need to manually upload releases, you can download the releases from the job artifacts and upload them with twine. You can also make the files locally (not recommended in general, as your local directory is more likely to be "dirty" and SDists love picking up random unrelated/hidden files); this is the procedure:
If you need to manually upload releases, you can download the releases from
the job artifacts and upload them with twine. You can also make the files
locally (not recommended in general, as your local directory is more likely
to be "dirty" and SDists love picking up random unrelated/hidden files);
this is the procedure:
.. code-block:: bash

View File

@ -8,6 +8,34 @@ to a new version. But it goes into more detail. This includes things like
deprecated APIs and their replacements, build system changes, general code
modernization and other useful information.
.. _upgrade-guide-2.12:
v2.12
=====
NumPy support has been upgraded to support the 2.x series too. The two relevant
changes are that:
* ``dtype.flags()`` is now a ``uint64`` and ``dtype.alignment()`` an
``ssize_t`` (and NumPy may return an larger than integer value for
``itemsize()`` in NumPy 2.x).
* The long deprecated NumPy function ``PyArray_GetArrayParamsFromObject``
function is not available anymore.
Due to NumPy changes, you may experience difficulties updating to NumPy 2.
Please see the [NumPy 2 migration guide](https://numpy.org/devdocs/numpy_2_0_migration_guide.html) for details.
For example, a more direct change could be that the default integer ``"int_"``
(and ``"uint"``) is now ``ssize_t`` and not ``long`` (affects 64bit windows).
If you want to only support NumPy 1.x for now and are having problems due to
the two internal changes listed above, you can define
``PYBIND11_NUMPY_1_ONLY`` to disable the new support for now. Make sure you
define this on all pybind11 compile units, since it could be a source of ODR
violations if used inconsistently. This option will be removed in the future,
so adapting your code is highly recommended.
.. _upgrade-guide-2.11:
v2.11

View File

@ -42,13 +42,15 @@ using make_caster = type_caster<intrinsic_t<type>>;
// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
template <typename T>
typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) {
return caster.operator typename make_caster<T>::template cast_op_type<T>();
using result_t = typename make_caster<T>::template cast_op_type<T>; // See PR #4893
return caster.operator result_t();
}
template <typename T>
typename make_caster<T>::template cast_op_type<typename std::add_rvalue_reference<T>::type>
cast_op(make_caster<T> &&caster) {
return std::move(caster).operator typename make_caster<T>::
template cast_op_type<typename std::add_rvalue_reference<T>::type>();
using result_t = typename make_caster<T>::template cast_op_type<
typename std::add_rvalue_reference<T>::type>; // See PR #4893
return std::move(caster).operator result_t();
}
template <typename type>
@ -325,8 +327,9 @@ public:
value = false;
return true;
}
if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) {
// (allow non-implicit conversion for numpy booleans)
if (convert || is_numpy_bool(src)) {
// (allow non-implicit conversion for numpy booleans), use strncmp
// since NumPy 1.x had an additional trailing underscore.
Py_ssize_t res = -1;
if (src.is_none()) {
@ -358,6 +361,15 @@ public:
return handle(src ? Py_True : Py_False).inc_ref();
}
PYBIND11_TYPE_CASTER(bool, const_name("bool"));
private:
// Test if an object is a NumPy boolean (without fetching the type).
static inline bool is_numpy_bool(handle object) {
const char *type_name = Py_TYPE(object.ptr())->tp_name;
// Name changed to `numpy.bool` in NumPy 2, `numpy.bool_` is needed for 1.x support
return std::strcmp("numpy.bool", type_name) == 0
|| std::strcmp("numpy.bool_", type_name) == 0;
}
};
// Helper class for UTF-{8,16,32} C++ stl strings:
@ -660,8 +672,9 @@ public:
return cast(*src, policy, parent);
}
static constexpr auto name
= const_name("Tuple[") + concat(make_caster<Ts>::name...) + const_name("]");
static constexpr auto name = const_name("tuple[")
+ ::pybind11::detail::concat(make_caster<Ts>::name...)
+ const_name("]");
template <typename T>
using cast_op_type = type;
@ -869,10 +882,53 @@ struct is_holder_type
template <typename base, typename deleter>
struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {};
#ifdef PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION // See PR #4888
// This leads to compilation errors if a specialization is missing.
template <typename T>
struct handle_type_name;
#else
template <typename T>
struct handle_type_name {
static constexpr auto name = const_name<T>();
};
#endif
template <>
struct handle_type_name<object> {
static constexpr auto name = const_name("object");
};
template <>
struct handle_type_name<list> {
static constexpr auto name = const_name("list");
};
template <>
struct handle_type_name<dict> {
static constexpr auto name = const_name("dict");
};
template <>
struct handle_type_name<anyset> {
static constexpr auto name = const_name("Union[set, frozenset]");
};
template <>
struct handle_type_name<set> {
static constexpr auto name = const_name("set");
};
template <>
struct handle_type_name<frozenset> {
static constexpr auto name = const_name("frozenset");
};
template <>
struct handle_type_name<str> {
static constexpr auto name = const_name("str");
};
template <>
struct handle_type_name<tuple> {
static constexpr auto name = const_name("tuple");
};
template <>
struct handle_type_name<bool_> {
static constexpr auto name = const_name("bool");
@ -882,6 +938,10 @@ struct handle_type_name<bytes> {
static constexpr auto name = const_name(PYBIND11_BYTES_NAME);
};
template <>
struct handle_type_name<buffer> {
static constexpr auto name = const_name("Buffer");
};
template <>
struct handle_type_name<int_> {
static constexpr auto name = const_name("int");
};
@ -898,10 +958,50 @@ struct handle_type_name<float_> {
static constexpr auto name = const_name("float");
};
template <>
struct handle_type_name<function> {
static constexpr auto name = const_name("Callable");
};
template <>
struct handle_type_name<handle> {
static constexpr auto name = handle_type_name<object>::name;
};
template <>
struct handle_type_name<none> {
static constexpr auto name = const_name("None");
};
template <>
struct handle_type_name<sequence> {
static constexpr auto name = const_name("Sequence");
};
template <>
struct handle_type_name<bytearray> {
static constexpr auto name = const_name("bytearray");
};
template <>
struct handle_type_name<memoryview> {
static constexpr auto name = const_name("memoryview");
};
template <>
struct handle_type_name<slice> {
static constexpr auto name = const_name("slice");
};
template <>
struct handle_type_name<type> {
static constexpr auto name = const_name("type");
};
template <>
struct handle_type_name<capsule> {
static constexpr auto name = const_name("capsule");
};
template <>
struct handle_type_name<ellipsis> {
static constexpr auto name = const_name("ellipsis");
};
template <>
struct handle_type_name<weakref> {
static constexpr auto name = const_name("weakref");
};
template <>
struct handle_type_name<args> {
static constexpr auto name = const_name("*args");
};
@ -909,6 +1009,30 @@ template <>
struct handle_type_name<kwargs> {
static constexpr auto name = const_name("**kwargs");
};
template <>
struct handle_type_name<obj_attr_accessor> {
static constexpr auto name = const_name<obj_attr_accessor>();
};
template <>
struct handle_type_name<str_attr_accessor> {
static constexpr auto name = const_name<str_attr_accessor>();
};
template <>
struct handle_type_name<item_accessor> {
static constexpr auto name = const_name<item_accessor>();
};
template <>
struct handle_type_name<sequence_accessor> {
static constexpr auto name = const_name<sequence_accessor>();
};
template <>
struct handle_type_name<list_accessor> {
static constexpr auto name = const_name<list_accessor>();
};
template <>
struct handle_type_name<tuple_accessor> {
static constexpr auto name = const_name<tuple_accessor>();
};
template <typename type>
struct pyobject_caster {
@ -1377,7 +1501,15 @@ inline namespace literals {
/** \rst
String literal version of `arg`
\endrst */
constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
constexpr arg
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
operator"" _a // gcc 4.8.5 insists on having a space (hard error).
#else
operator""_a // clang 17 generates a deprecation warning if there is a space.
#endif
(const char *name, size_t) {
return arg(name);
}
} // namespace literals
PYBIND11_NAMESPACE_BEGIN(detail)
@ -1438,7 +1570,8 @@ public:
static_assert(args_pos == -1 || args_pos == constexpr_first<argument_is_args, Args...>(),
"py::args cannot be specified more than once");
static constexpr auto arg_names = concat(type_descr(make_caster<Args>::name)...);
static constexpr auto arg_names
= ::pybind11::detail::concat(type_descr(make_caster<Args>::name)...);
bool load_args(function_call &call) { return load_impl_sequence(call, indices{}); }

View File

@ -86,17 +86,16 @@ inline PyTypeObject *make_static_property_type() {
type->tp_descr_get = pybind11_static_get;
type->tp_descr_set = pybind11_static_set;
if (PyType_Ready(type) < 0) {
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
}
# if PY_VERSION_HEX >= 0x030C0000
// PRE 3.12 FEATURE FREEZE. PLEASE REVIEW AFTER FREEZE.
// Since Python-3.12 property-derived types are required to
// have dynamic attributes (to set `__doc__`)
enable_dynamic_attributes(heap_type);
# endif
if (PyType_Ready(type) < 0) {
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
}
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
@ -189,12 +188,10 @@ extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, P
return nullptr;
}
// This must be a pybind11 instance
auto *instance = reinterpret_cast<detail::instance *>(self);
// Ensure that the base __init__ function(s) were called
for (const auto &vh : values_and_holders(instance)) {
if (!vh.holder_constructed()) {
values_and_holders vhs(self);
for (const auto &vh : vhs) {
if (!vh.holder_constructed() && !vhs.is_redundant_value_and_holder(vh)) {
PyErr_Format(PyExc_TypeError,
"%.200s.__init__() must be called when overriding __init__",
get_fully_qualified_tp_name(vh.type->type).c_str());
@ -375,7 +372,7 @@ extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *,
extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) {
PyTypeObject *type = Py_TYPE(self);
std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!";
PyErr_SetString(PyExc_TypeError, msg.c_str());
set_error(PyExc_TypeError, msg.c_str());
return -1;
}
@ -522,8 +519,12 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
#if PY_VERSION_HEX >= 0x030D0000
PyObject_VisitManagedDict(self, visit, arg);
#else
PyObject *&dict = *_PyObject_GetDictPtr(self);
Py_VISIT(dict);
#endif
// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
#if PY_VERSION_HEX >= 0x03090000
Py_VISIT(Py_TYPE(self));
@ -533,8 +534,12 @@ extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *a
/// dynamic_attr: Allow the GC to clear the dictionary.
extern "C" inline int pybind11_clear(PyObject *self) {
#if PY_VERSION_HEX >= 0x030D0000
PyObject_ClearManagedDict(self);
#else
PyObject *&dict = *_PyObject_GetDictPtr(self);
Py_CLEAR(dict);
#endif
return 0;
}
@ -579,7 +584,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
if (view) {
view->obj = nullptr;
}
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
set_error(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
return -1;
}
std::memset(view, 0, sizeof(Py_buffer));
@ -587,7 +592,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
delete info;
// view->obj = nullptr; // Was just memset to 0, so not necessary
PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
set_error(PyExc_BufferError, "Writable buffer requested for readonly storage");
return -1;
}
view->obj = obj;

View File

@ -10,12 +10,12 @@
#pragma once
#define PYBIND11_VERSION_MAJOR 2
#define PYBIND11_VERSION_MINOR 11
#define PYBIND11_VERSION_PATCH 1
#define PYBIND11_VERSION_MINOR 12
#define PYBIND11_VERSION_PATCH 0
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
// Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x020B0100
#define PYBIND11_VERSION_HEX 0x020C0000
// Define some generic pybind11 helper macros for warning management.
//
@ -118,6 +118,14 @@
# endif
#endif
#if defined(PYBIND11_CPP20)
# define PYBIND11_CONSTINIT constinit
# define PYBIND11_DTOR_CONSTEXPR constexpr
#else
# define PYBIND11_CONSTINIT
# define PYBIND11_DTOR_CONSTEXPR
#endif
// Compiler version assertions
#if defined(__INTEL_COMPILER)
# if __INTEL_COMPILER < 1800
@ -288,6 +296,10 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
# undef copysign
#endif
#if defined(PYBIND11_NUMPY_1_ONLY)
# define PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED
#endif
#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# define PYBIND11_SIMPLE_GIL_MANAGEMENT
#endif
@ -399,7 +411,7 @@ PYBIND11_WARNING_POP
return nullptr; \
} \
catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
::pybind11::set_error(PyExc_ImportError, e.what()); \
return nullptr; \
}

View File

@ -65,7 +65,7 @@ constexpr bool is_alias(void *) {
}
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
// back to brace aggregate initiailization so that for aggregate initialization can be used with
// back to brace aggregate initialization so that for aggregate initialization can be used with
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).

View File

@ -34,8 +34,9 @@
/// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version.
#ifndef PYBIND11_INTERNALS_VERSION
# if PY_VERSION_HEX >= 0x030C0000
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
// Version bump for Python 3.12+, before first 3.12 beta release.
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
# define PYBIND11_INTERNALS_VERSION 5
# else
# define PYBIND11_INTERNALS_VERSION 4
@ -66,9 +67,14 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
// `Py_LIMITED_API` anyway.
# if PYBIND11_INTERNALS_VERSION > 4
# define PYBIND11_TLS_KEY_REF Py_tss_t &
# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
// for every field.
# if defined(__clang__)
# define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("clang diagnostic push") /**/ \
_Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
Py_tss_t var \
= Py_tss_NEEDS_INIT; \
_Pragma("clang diagnostic pop")
# elif defined(__GNUC__) && !defined(__INTEL_COMPILER)
# define PYBIND11_TLS_KEY_INIT(var) \
_Pragma("GCC diagnostic push") /**/ \
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
@ -291,9 +297,12 @@ struct type_info {
#endif
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
/// On MSVC, changes in _MSC_VER may indicate ABI incompatibility (#2898).
#ifndef PYBIND11_BUILD_ABI
# if defined(__GXX_ABI_VERSION)
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
# elif defined(_MSC_VER)
# define PYBIND11_BUILD_ABI "_mscver" PYBIND11_TOSTRING(_MSC_VER)
# else
# define PYBIND11_BUILD_ABI ""
# endif
@ -352,7 +361,7 @@ inline bool raise_err(PyObject *exc_type, const char *msg) {
raise_from(exc_type, msg);
return true;
}
PyErr_SetString(exc_type, msg);
set_error(exc_type, msg);
return false;
}
@ -447,6 +456,7 @@ inline object get_python_state_dict() {
#endif
if (!state_dict) {
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
throw error_already_set();
}
return state_dict;
}
@ -459,6 +469,7 @@ inline internals **get_internals_pp_from_capsule(handle obj) {
void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr);
if (raw_ptr == nullptr) {
raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED");
throw error_already_set();
}
return static_cast<internals **>(raw_ptr);
}

View File

@ -102,8 +102,22 @@ public:
inline std::pair<decltype(internals::registered_types_py)::iterator, bool>
all_type_info_get_cache(PyTypeObject *type);
// Band-aid workaround to fix a subtle but serious bug in a minimalistic fashion. See PR #4762.
inline void all_type_info_add_base_most_derived_first(std::vector<type_info *> &bases,
type_info *addl_base) {
for (auto it = bases.begin(); it != bases.end(); it++) {
type_info *existing_base = *it;
if (PyType_IsSubtype(addl_base->type, existing_base->type) != 0) {
bases.insert(it, addl_base);
return;
}
}
bases.push_back(addl_base);
}
// Populates a just-created cache entry.
PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
assert(bases.empty());
std::vector<PyTypeObject *> check;
for (handle parent : reinterpret_borrow<tuple>(t->tp_bases)) {
check.push_back((PyTypeObject *) parent.ptr());
@ -136,7 +150,7 @@ PYBIND11_NOINLINE void all_type_info_populate(PyTypeObject *t, std::vector<type_
}
}
if (!found) {
bases.push_back(tinfo);
all_type_info_add_base_most_derived_first(bases, tinfo);
}
}
} else if (type->tp_bases) {
@ -322,18 +336,29 @@ public:
explicit values_and_holders(instance *inst)
: inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {}
explicit values_and_holders(PyObject *obj)
: inst{nullptr}, tinfo(all_type_info(Py_TYPE(obj))) {
if (!tinfo.empty()) {
inst = reinterpret_cast<instance *>(obj);
}
}
struct iterator {
private:
instance *inst = nullptr;
const type_vec *types = nullptr;
value_and_holder curr;
friend struct values_and_holders;
iterator(instance *inst, const type_vec *tinfo)
: inst{inst}, types{tinfo},
curr(inst /* instance */,
types->empty() ? nullptr : (*types)[0] /* type info */,
iterator(instance *inst, const type_vec *tinfo) : inst{inst}, types{tinfo} {
if (inst != nullptr) {
assert(!types->empty());
curr = value_and_holder(
inst /* instance */,
(*types)[0] /* type info */,
0, /* vpos: (non-simple types only): the first vptr comes first */
0 /* index */) {}
0 /* index */);
}
}
// Past-the-end iterator:
explicit iterator(size_t end) : curr(end) {}
@ -364,6 +389,16 @@ public:
}
size_t size() { return tinfo.size(); }
// Band-aid workaround to fix a subtle but serious bug in a minimalistic fashion. See PR #4762.
bool is_redundant_value_and_holder(const value_and_holder &vh) {
for (size_t i = 0; i < vh.index; i++) {
if (PyType_IsSubtype(tinfo[i]->type, tinfo[vh.index]->type) != 0) {
return true;
}
}
return false;
}
};
/**
@ -486,8 +521,10 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
inline PyThreadState *get_thread_state_unchecked() {
#if defined(PYPY_VERSION)
return PyThreadState_GET();
#else
#elif PY_VERSION_HEX < 0x030D0000
return _PyThreadState_UncheckedGet();
#else
return PyThreadState_GetUnchecked();
#endif
}
@ -786,7 +823,7 @@ public:
std::string tname = rtti_type ? rtti_type->name() : cast_type.name();
detail::clean_type_id(tname);
std::string msg = "Unregistered type : " + tname;
PyErr_SetString(PyExc_TypeError, msg.c_str());
set_error(PyExc_TypeError, msg.c_str());
return {nullptr, nullptr};
}
@ -1164,13 +1201,17 @@ protected:
static Constructor make_move_constructor(...) { return nullptr; }
};
inline std::string quote_cpp_type_name(const std::string &cpp_type_name) {
return cpp_type_name; // No-op for now. See PR #4888
}
PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) {
if (auto *type_data = get_type_info(ti)) {
handle th((PyObject *) type_data->type);
return th.attr("__module__").cast<std::string>() + '.'
+ th.attr("__qualname__").cast<std::string>();
}
return clean_type_id(ti.name());
return quote_cpp_type_name(clean_type_id(ti.name()));
}
PYBIND11_NAMESPACE_END(detail)

View File

@ -70,7 +70,7 @@ struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexTy
template <size_t... Is>
struct helper<index_sequence<Is...>> {
static constexpr auto value = concat(const_name(((void) Is, "?"))...);
static constexpr auto value = ::pybind11::detail::concat(const_name(((void) Is, "?"))...);
};
static constexpr auto dimensions_descriptor
@ -104,7 +104,8 @@ struct eigen_tensor_helper<
return get_shape() == shape;
}
static constexpr auto dimensions_descriptor = concat(const_name<Indices>()...);
static constexpr auto dimensions_descriptor
= ::pybind11::detail::concat(const_name<Indices>()...);
template <typename... Args>
static Type *alloc(Args &&...args) {

View File

@ -128,7 +128,8 @@ public:
}
PYBIND11_TYPE_CASTER(type,
const_name("Callable[[") + concat(make_caster<Args>::name...)
const_name("Callable[[")
+ ::pybind11::detail::concat(make_caster<Args>::name...)
+ const_name("], ") + make_caster<retval_type>::name
+ const_name("]"));
};

View File

@ -11,6 +11,8 @@
#include "detail/common.h"
#include <cassert>
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
# include "detail/internals.h"
#endif
@ -137,7 +139,9 @@ private:
class gil_scoped_release {
public:
// PRECONDITION: The GIL must be held when this constructor is called.
explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
assert(PyGILState_Check());
// `get_internals()` must be called here unconditionally in order to initialize
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
// initialization race could occur as multiple threads try `gil_scoped_acquire`.
@ -201,7 +205,11 @@ class gil_scoped_release {
PyThreadState *state;
public:
gil_scoped_release() : state{PyEval_SaveThread()} {}
// PRECONDITION: The GIL must be held when this constructor is called.
gil_scoped_release() {
assert(PyGILState_Check());
state = PyEval_SaveThread();
}
gil_scoped_release(const gil_scoped_release &) = delete;
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
~gil_scoped_release() { PyEval_RestoreThread(state); }

View File

@ -0,0 +1,91 @@
// Copyright (c) 2023 The pybind Community.
#pragma once
#include "detail/common.h"
#include "gil.h"
#include <cassert>
#include <mutex>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Use the `gil_safe_call_once_and_store` class below instead of the naive
//
// static auto imported_obj = py::module_::import("module_name"); // BAD, DO NOT USE!
//
// which has two serious issues:
//
// 1. Py_DECREF() calls potentially after the Python interpreter was finalized already, and
// 2. deadlocks in multi-threaded processes (because of missing lock ordering).
//
// The following alternative avoids both problems:
//
// PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
// auto &imported_obj = storage // Do NOT make this `static`!
// .call_once_and_store_result([]() {
// return py::module_::import("module_name");
// })
// .get_stored();
//
// The parameter of `call_once_and_store_result()` must be callable. It can make
// CPython API calls, and in particular, it can temporarily release the GIL.
//
// `T` can be any C++ type, it does not have to involve CPython API types.
//
// The behavior with regard to signals, e.g. `SIGINT` (`KeyboardInterrupt`),
// is not ideal. If the main thread is the one to actually run the `Callable`,
// then a `KeyboardInterrupt` will interrupt it if it is running normal Python
// code. The situation is different if a non-main thread runs the
// `Callable`, and then the main thread starts waiting for it to complete:
// a `KeyboardInterrupt` will not interrupt the non-main thread, but it will
// get processed only when it is the main thread's turn again and it is running
// normal Python code. However, this will be unnoticeable for quick call-once
// functions, which is usually the case.
template <typename T>
class gil_safe_call_once_and_store {
public:
// PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called.
template <typename Callable>
gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn) {
if (!is_initialized_) { // This read is guarded by the GIL.
// Multiple threads may enter here, because the GIL is released in the next line and
// CPython API calls in the `fn()` call below may release and reacquire the GIL.
gil_scoped_release gil_rel; // Needed to establish lock ordering.
std::call_once(once_flag_, [&] {
// Only one thread will ever enter here.
gil_scoped_acquire gil_acq;
::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL.
is_initialized_ = true; // This write is guarded by the GIL.
});
// All threads will observe `is_initialized_` as true here.
}
// Intentionally not returning `T &` to ensure the calling code is self-documenting.
return *this;
}
// This must only be called after `call_once_and_store_result()` was called.
T &get_stored() {
assert(is_initialized_);
PYBIND11_WARNING_PUSH
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
// Needed for gcc 4.8.5
PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
#endif
return *reinterpret_cast<T *>(storage_);
PYBIND11_WARNING_POP
}
constexpr gil_safe_call_once_and_store() = default;
PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default;
private:
alignas(T) char storage_[sizeof(T)] = {};
std::once_flag once_flag_ = {};
bool is_initialized_ = false;
// The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
// but the latter does not have the triviality properties of former,
// therefore `std::optional` is not a viable alternative here.
};
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -10,7 +10,10 @@
#pragma once
#include "pybind11.h"
#include "detail/common.h"
#include "complex.h"
#include "gil_safe_call_once.h"
#include "pytypes.h"
#include <algorithm>
#include <array>
@ -26,10 +29,15 @@
#include <utility>
#include <vector>
#if defined(PYBIND11_NUMPY_1_ONLY) && !defined(PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED)
# error PYBIND11_NUMPY_1_ONLY must be defined before any pybind11 header is included.
#endif
/* This will be true on all flat address space platforms and allows us to reduce the
whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
and dimension types (e.g. shape, strides, indexing), instead of inflicting this
upon the library user. */
upon the library user.
Note that NumPy 2 now uses ssize_t for `npy_intp` to simplify this. */
static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t");
static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
// We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares)
@ -38,10 +46,16 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127)
class dtype; // Forward declaration
class array; // Forward declaration
PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<dtype> {
static constexpr auto name = const_name("numpy.dtype");
};
template <>
struct handle_type_name<array> {
static constexpr auto name = const_name("numpy.ndarray");
@ -50,7 +64,8 @@ struct handle_type_name<array> {
template <typename type, typename SFINAE = void>
struct npy_format_descriptor;
struct PyArrayDescr_Proxy {
/* NumPy 1 proxy (always includes legacy fields) */
struct PyArrayDescr1_Proxy {
PyObject_HEAD
PyObject *typeobj;
char kind;
@ -65,6 +80,43 @@ struct PyArrayDescr_Proxy {
PyObject *names;
};
#ifndef PYBIND11_NUMPY_1_ONLY
struct PyArrayDescr_Proxy {
PyObject_HEAD
PyObject *typeobj;
char kind;
char type;
char byteorder;
char _former_flags;
int type_num;
/* Additional fields are NumPy version specific. */
};
#else
/* NumPy 1.x only, we can expose all fields */
using PyArrayDescr_Proxy = PyArrayDescr1_Proxy;
#endif
/* NumPy 2 proxy, including legacy fields */
struct PyArrayDescr2_Proxy {
PyObject_HEAD
PyObject *typeobj;
char kind;
char type;
char byteorder;
char _former_flags;
int type_num;
std::uint64_t flags;
ssize_t elsize;
ssize_t alignment;
PyObject *metadata;
Py_hash_t hash;
void *reserved_null[2];
/* The following fields only exist if 0 <= type_num < 2056 */
char *subarray;
PyObject *fields;
PyObject *names;
};
struct PyArray_Proxy {
PyObject_HEAD
char *data;
@ -120,6 +172,28 @@ inline numpy_internals &get_numpy_internals() {
return *ptr;
}
PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) {
module_ numpy = module_::import("numpy");
str version_string = numpy.attr("__version__");
module_ numpy_lib = module_::import("numpy.lib");
object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
int major_version = numpy_version.attr("major").cast<int>();
#ifdef PYBIND11_NUMPY_1_ONLY
if (major_version >= 2) {
throw std::runtime_error(
"This extension was built with PYBIND11_NUMPY_1_ONLY defined, "
"but NumPy 2 is used in this process. For NumPy2 compatibility, "
"this extension needs to be rebuilt without the PYBIND11_NUMPY_1_ONLY define.");
}
#endif
/* `numpy.core` was renamed to `numpy._core` in NumPy 2.0 as it officially
became a private module. */
std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core";
return module_::import((numpy_core_path + "." + submodule_name).c_str());
}
template <typename T>
struct same_size {
template <typename U>
@ -186,14 +260,16 @@ struct npy_api {
NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
};
unsigned int PyArray_RUNTIME_VERSION_;
struct PyArray_Dims {
Py_intptr_t *ptr;
int len;
};
static npy_api &get() {
static npy_api api = lookup();
return api;
PYBIND11_CONSTINIT static gil_safe_call_once_and_store<npy_api> storage;
return storage.call_once_and_store_result(lookup).get_stored();
}
bool PyArray_Check_(PyObject *obj) const {
@ -224,6 +300,7 @@ struct npy_api {
PyObject *(*PyArray_FromAny_)(PyObject *, PyObject *, int, int, int, PyObject *);
int (*PyArray_DescrConverter_)(PyObject *, PyObject **);
bool (*PyArray_EquivTypes_)(PyObject *, PyObject *);
#ifdef PYBIND11_NUMPY_1_ONLY
int (*PyArray_GetArrayParamsFromObject_)(PyObject *,
PyObject *,
unsigned char,
@ -232,6 +309,7 @@ struct npy_api {
Py_intptr_t *,
PyObject **,
PyObject *);
#endif
PyObject *(*PyArray_Squeeze_)(PyObject *);
// Unused. Not removed because that affects ABI of the class.
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
@ -249,7 +327,8 @@ private:
API_PyArray_DescrFromScalar = 57,
API_PyArray_FromAny = 69,
API_PyArray_Resize = 80,
API_PyArray_CopyInto = 82,
// CopyInto was slot 82 and 50 was effectively an alias. NumPy 2 removed 82.
API_PyArray_CopyInto = 50,
API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 96,
@ -258,18 +337,25 @@ private:
API_PyArray_View = 137,
API_PyArray_DescrConverter = 174,
API_PyArray_EquivTypes = 182,
#ifdef PYBIND11_NUMPY_1_ONLY
API_PyArray_GetArrayParamsFromObject = 278,
#endif
API_PyArray_SetBaseObject = 282
};
static npy_api lookup() {
module_ m = module_::import("numpy.core.multiarray");
module_ m = detail::import_numpy_core_submodule("multiarray");
auto c = m.attr("_ARRAY_API");
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
if (api_ptr == nullptr) {
raise_from(PyExc_SystemError, "FAILURE obtaining numpy _ARRAY_API pointer.");
throw error_already_set();
}
npy_api api;
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) {
api.PyArray_RUNTIME_VERSION_ = api.PyArray_GetNDArrayCFeatureVersion_();
if (api.PyArray_RUNTIME_VERSION_ < 0x7) {
pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0");
}
DECL_NPY_API(PyArray_Type);
@ -288,7 +374,9 @@ private:
DECL_NPY_API(PyArray_View);
DECL_NPY_API(PyArray_DescrConverter);
DECL_NPY_API(PyArray_EquivTypes);
#ifdef PYBIND11_NUMPY_1_ONLY
DECL_NPY_API(PyArray_GetArrayParamsFromObject);
#endif
DECL_NPY_API(PyArray_SetBaseObject);
#undef DECL_NPY_API
@ -310,6 +398,14 @@ inline const PyArrayDescr_Proxy *array_descriptor_proxy(const PyObject *ptr) {
return reinterpret_cast<const PyArrayDescr_Proxy *>(ptr);
}
inline const PyArrayDescr1_Proxy *array_descriptor1_proxy(const PyObject *ptr) {
return reinterpret_cast<const PyArrayDescr1_Proxy *>(ptr);
}
inline const PyArrayDescr2_Proxy *array_descriptor2_proxy(const PyObject *ptr) {
return reinterpret_cast<const PyArrayDescr2_Proxy *>(ptr);
}
inline bool check_flags(const void *ptr, int flag) {
return (flag == (array_proxy(ptr)->flags & flag));
}
@ -350,7 +446,7 @@ struct array_info<std::array<T, N>> {
}
static constexpr auto extents = const_name<array_info<T>::is_array>(
concat(const_name<N>(), array_info<T>::extents), const_name<N>());
::pybind11::detail::concat(const_name<N>(), array_info<T>::extents), const_name<N>());
};
// For numpy we have special handling for arrays of characters, so we don't include
// the size in the array extents.
@ -589,10 +685,32 @@ public:
}
/// Size of the data type in bytes.
#ifdef PYBIND11_NUMPY_1_ONLY
ssize_t itemsize() const { return detail::array_descriptor_proxy(m_ptr)->elsize; }
#else
ssize_t itemsize() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return detail::array_descriptor1_proxy(m_ptr)->elsize;
}
return detail::array_descriptor2_proxy(m_ptr)->elsize;
}
#endif
/// Returns true for structured data types.
#ifdef PYBIND11_NUMPY_1_ONLY
bool has_fields() const { return detail::array_descriptor_proxy(m_ptr)->names != nullptr; }
#else
bool has_fields() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return detail::array_descriptor1_proxy(m_ptr)->names != nullptr;
}
const auto *proxy = detail::array_descriptor2_proxy(m_ptr);
if (proxy->type_num < 0 || proxy->type_num >= 2056) {
return false;
}
return proxy->names != nullptr;
}
#endif
/// Single-character code for dtype's kind.
/// For example, floating point types are 'f' and integral types are 'i'.
@ -619,19 +737,38 @@ public:
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
/// Alignment of the data type
#ifdef PYBIND11_NUMPY_1_ONLY
int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; }
#else
ssize_t alignment() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return detail::array_descriptor1_proxy(m_ptr)->alignment;
}
return detail::array_descriptor2_proxy(m_ptr)->alignment;
}
#endif
/// Flags for the array descriptor
#ifdef PYBIND11_NUMPY_1_ONLY
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
#else
std::uint64_t flags() const {
if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
return (unsigned char) detail::array_descriptor1_proxy(m_ptr)->flags;
}
return detail::array_descriptor2_proxy(m_ptr)->flags;
}
#endif
private:
static object _dtype_from_pep3118() {
static PyObject *obj = module_::import("numpy.core._internal")
.attr("_dtype_from_pep3118")
.cast<object>()
.release()
.ptr();
return reinterpret_borrow<object>(obj);
static object &_dtype_from_pep3118() {
PYBIND11_CONSTINIT static gil_safe_call_once_and_store<object> storage;
return storage
.call_once_and_store_result([]() {
return detail::import_numpy_core_submodule("_internal")
.attr("_dtype_from_pep3118");
})
.get_stored();
}
dtype strip_padding(ssize_t itemsize) {
@ -788,9 +925,7 @@ public:
}
/// Byte size of a single element
ssize_t itemsize() const {
return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize;
}
ssize_t itemsize() const { return dtype().itemsize(); }
/// Total number of bytes
ssize_t nbytes() const { return size() * itemsize(); }
@ -1008,7 +1143,7 @@ protected:
/// Create array from any object -- always returns a new reference
static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) {
if (ptr == nullptr) {
PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr");
set_error(PyExc_ValueError, "cannot create a pybind11::array from a nullptr");
return nullptr;
}
return detail::npy_api::get().PyArray_FromAny_(
@ -1155,7 +1290,7 @@ protected:
/// Create array from any object -- always returns a new reference
static PyObject *raw_array_t(PyObject *ptr) {
if (ptr == nullptr) {
PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr");
set_error(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr");
return nullptr;
}
return detail::npy_api::get().PyArray_FromAny_(ptr,

View File

@ -14,7 +14,9 @@
#include "detail/init.h"
#include "attr.h"
#include "gil.h"
#include "gil_safe_call_once.h"
#include "options.h"
#include "typing.h"
#include <cstdlib>
#include <cstring>
@ -52,6 +54,47 @@ PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail)
inline std::string replace_newlines_and_squash(const char *text) {
const char *whitespaces = " \t\n\r\f\v";
std::string result(text);
bool previous_is_whitespace = false;
if (result.size() >= 2) {
// Do not modify string representations
char first_char = result[0];
char last_char = result[result.size() - 1];
if (first_char == last_char && first_char == '\'') {
return result;
}
}
result.clear();
// Replace characters in whitespaces array with spaces and squash consecutive spaces
while (*text != '\0') {
if (std::strchr(whitespaces, *text)) {
if (!previous_is_whitespace) {
result += ' ';
previous_is_whitespace = true;
}
} else {
result += *text;
previous_is_whitespace = false;
}
++text;
}
// Strip leading and trailing whitespaces
const size_t str_begin = result.find_first_not_of(whitespaces);
if (str_begin == std::string::npos) {
return "";
}
const size_t str_end = result.find_last_not_of(whitespaces);
const size_t str_range = str_end - str_begin + 1;
return result.substr(str_begin, str_range);
}
// Apply all the extensions translators from a list
// Return true if one of the translators completed without raising an exception
// itself. Return of false indicates that if there are other translators
@ -424,7 +467,7 @@ protected:
// Write default value if available.
if (!is_starred && arg_index < rec->args.size() && rec->args[arg_index].descr) {
signature += " = ";
signature += rec->args[arg_index].descr;
signature += detail::replace_newlines_and_squash(rec->args[arg_index].descr);
}
// Separator for positional-only arguments (placed after the
// argument, rather than before like *
@ -449,9 +492,7 @@ protected:
signature += rec->scope.attr("__module__").cast<std::string>() + "."
+ rec->scope.attr("__qualname__").cast<std::string>();
} else {
std::string tname(t->name());
detail::clean_type_id(tname);
signature += tname;
signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
}
} else {
signature += c;
@ -680,7 +721,7 @@ protected:
/* Iterator over the list of potentially admissible overloads */
const function_record *overloads = reinterpret_cast<function_record *>(
PyCapsule_GetPointer(self, get_function_record_capsule_name())),
*it = overloads;
*current_overload = overloads;
assert(overloads != nullptr);
/* Need to know how many arguments + keyword arguments there are to pick the right
@ -694,8 +735,7 @@ protected:
if (overloads->is_constructor) {
if (!parent
|| !PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) {
PyErr_SetString(
PyExc_TypeError,
set_error(PyExc_TypeError,
"__init__(self, ...) called with invalid or missing `self` argument");
return nullptr;
}
@ -719,9 +759,10 @@ protected:
std::vector<function_call> second_pass;
// However, if there are no overloads, we can just skip the no-convert pass entirely
const bool overloaded = it != nullptr && it->next != nullptr;
const bool overloaded
= current_overload != nullptr && current_overload->next != nullptr;
for (; it != nullptr; it = it->next) {
for (; current_overload != nullptr; current_overload = current_overload->next) {
/* For each overload:
1. Copy all positional arguments we were given, also checking to make sure that
@ -742,7 +783,7 @@ protected:
a result other than PYBIND11_TRY_NEXT_OVERLOAD.
*/
const function_record &func = *it;
const function_record &func = *current_overload;
size_t num_args = func.nargs; // Number of positional arguments that we need
if (func.has_args) {
--num_args; // (but don't count py::args
@ -980,10 +1021,10 @@ protected:
}
if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) {
// The error reporting logic below expects 'it' to be valid, as it would be
// if we'd encountered this failure in the first-pass loop.
// The error reporting logic below expects 'current_overload' to be valid,
// as it would be if we'd encountered this failure in the first-pass loop.
if (!result) {
it = &call.func;
current_overload = &call.func;
}
break;
}
@ -1007,7 +1048,7 @@ protected:
A translator may choose to do one of the following:
- catch the exception and call PyErr_SetString or PyErr_SetObject
- catch the exception and call py::set_error()
to set a standard (or custom) Python exception, or
- do nothing and let the exception fall through to the next translator, or
- delegate translation to the next translator by throwing a new type of exception.
@ -1023,8 +1064,7 @@ protected:
return nullptr;
}
PyErr_SetString(PyExc_SystemError,
"Exception escaped from default exception translator!");
set_error(PyExc_SystemError, "Exception escaped from default exception translator!");
return nullptr;
}
@ -1102,7 +1142,7 @@ protected:
}
msg += "kwargs: ";
bool first = true;
for (auto kwarg : kwargs) {
for (const auto &kwarg : kwargs) {
if (first) {
first = false;
} else {
@ -1125,20 +1165,21 @@ protected:
raise_from(PyExc_TypeError, msg.c_str());
return nullptr;
}
PyErr_SetString(PyExc_TypeError, msg.c_str());
set_error(PyExc_TypeError, msg.c_str());
return nullptr;
}
if (!result) {
std::string msg = "Unable to convert function return value to a "
"Python type! The signature was\n\t";
msg += it->signature;
assert(current_overload != nullptr);
msg += current_overload->signature;
append_note_if_missing_header_is_suspected(msg);
// Attach additional error info to the exception if supported
if (PyErr_Occurred()) {
raise_from(PyExc_TypeError, msg.c_str());
return nullptr;
}
PyErr_SetString(PyExc_TypeError, msg.c_str());
set_error(PyExc_TypeError, msg.c_str());
return nullptr;
}
if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) {
@ -1149,6 +1190,15 @@ protected:
}
};
PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<cpp_function> {
static constexpr auto name = const_name("Callable");
};
PYBIND11_NAMESPACE_END(detail)
/// Wrapper for Python extension modules
class module_ : public object {
public:
@ -1276,6 +1326,15 @@ public:
}
};
PYBIND11_NAMESPACE_BEGIN(detail)
template <>
struct handle_type_name<module_> {
static constexpr auto name = const_name("module");
};
PYBIND11_NAMESPACE_END(detail)
// When inside a namespace (or anywhere as long as it's not the first item on a line),
// C++20 allows "module" to be used. This is provided for backward compatibility, and for
// simplicity, if someone wants to use py::module for example, that is perfectly safe.
@ -1977,7 +2036,7 @@ struct enum_base {
object type_name = type::handle_of(arg).attr("__name__");
return pybind11::str("{}.{}").format(std::move(type_name), enum_name(arg));
},
name("name"),
name("__str__"),
is_method(m_base));
if (options::show_enum_members_docstring()) {
@ -2395,7 +2454,7 @@ iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&...extra) {
Policy);
}
return cast(state{first, last, true});
return cast(state{std::forward<Iterator>(first), std::forward<Sentinel>(last), true});
}
PYBIND11_NAMESPACE_END(detail)
@ -2406,13 +2465,15 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
typename Sentinel,
typename ValueType = typename detail::iterator_access<Iterator>::result_type,
typename... Extra>
iterator make_iterator(Iterator first, Sentinel last, Extra &&...extra) {
typing::Iterator<ValueType> make_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<detail::iterator_access<Iterator>,
Policy,
Iterator,
Sentinel,
ValueType,
Extra...>(first, last, std::forward<Extra>(extra)...);
Extra...>(std::forward<Iterator>(first),
std::forward<Sentinel>(last),
std::forward<Extra>(extra)...);
}
/// Makes a python iterator over the keys (`.first`) of a iterator over pairs from a
@ -2422,13 +2483,15 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
typename Sentinel,
typename KeyType = typename detail::iterator_key_access<Iterator>::result_type,
typename... Extra>
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
typing::Iterator<KeyType> make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<detail::iterator_key_access<Iterator>,
Policy,
Iterator,
Sentinel,
KeyType,
Extra...>(first, last, std::forward<Extra>(extra)...);
Extra...>(std::forward<Iterator>(first),
std::forward<Sentinel>(last),
std::forward<Extra>(extra)...);
}
/// Makes a python iterator over the values (`.second`) of a iterator over pairs from a
@ -2438,21 +2501,25 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
typename Sentinel,
typename ValueType = typename detail::iterator_value_access<Iterator>::result_type,
typename... Extra>
iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
typing::Iterator<ValueType> make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<detail::iterator_value_access<Iterator>,
Policy,
Iterator,
Sentinel,
ValueType,
Extra...>(first, last, std::forward<Extra>(extra)...);
Extra...>(std::forward<Iterator>(first),
std::forward<Sentinel>(last),
std::forward<Extra>(extra)...);
}
/// Makes an iterator over values of an stl container or other container supporting
/// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type,
typename ValueType = typename detail::iterator_access<
decltype(std::begin(std::declval<Type &>()))>::result_type,
typename... Extra>
iterator make_iterator(Type &value, Extra &&...extra) {
typing::Iterator<ValueType> make_iterator(Type &value, Extra &&...extra) {
return make_iterator<Policy>(
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
}
@ -2461,8 +2528,10 @@ iterator make_iterator(Type &value, Extra &&...extra) {
/// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type,
typename KeyType = typename detail::iterator_key_access<
decltype(std::begin(std::declval<Type &>()))>::result_type,
typename... Extra>
iterator make_key_iterator(Type &value, Extra &&...extra) {
typing::Iterator<KeyType> make_key_iterator(Type &value, Extra &&...extra) {
return make_key_iterator<Policy>(
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
}
@ -2471,8 +2540,10 @@ iterator make_key_iterator(Type &value, Extra &&...extra) {
/// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type,
typename ValueType = typename detail::iterator_value_access<
decltype(std::begin(std::declval<Type &>()))>::result_type,
typename... Extra>
iterator make_value_iterator(Type &value, Extra &&...extra) {
typing::Iterator<ValueType> make_value_iterator(Type &value, Extra &&...extra) {
return make_value_iterator<Policy>(
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
}
@ -2528,7 +2599,7 @@ inline void register_local_exception_translator(ExceptionTranslator &&translator
/**
* Wrapper to generate a new Python exception type.
*
* This should only be used with PyErr_SetString for now.
* This should only be used with py::set_error() for now.
* It is not (yet) possible to use as a py::base.
* Template type argument is reserved for future use.
*/
@ -2549,27 +2620,25 @@ public:
}
// Sets the current python exception to this exception object with the given message
void operator()(const char *message) { PyErr_SetString(m_ptr, message); }
PYBIND11_DEPRECATED("Please use py::set_error() instead "
"(https://github.com/pybind/pybind11/pull/4772)")
void operator()(const char *message) const { set_error(*this, message); }
};
PYBIND11_NAMESPACE_BEGIN(detail)
// Returns a reference to a function-local static exception object used in the simple
// register_exception approach below. (It would be simpler to have the static local variable
// directly in register_exception, but that makes clang <3.5 segfault - issue #1349).
template <typename CppException>
exception<CppException> &get_exception_object() {
static exception<CppException> ex;
return ex;
}
template <>
struct handle_type_name<exception<void>> {
static constexpr auto name = const_name("Exception");
};
// Helper function for register_exception and register_local_exception
template <typename CppException>
exception<CppException> &
register_exception_impl(handle scope, const char *name, handle base, bool isLocal) {
auto &ex = detail::get_exception_object<CppException>();
if (!ex) {
ex = exception<CppException>(scope, name, base);
}
PYBIND11_CONSTINIT static gil_safe_call_once_and_store<exception<CppException>> exc_storage;
exc_storage.call_once_and_store_result(
[&]() { return exception<CppException>(scope, name, base); });
auto register_func
= isLocal ? &register_local_exception_translator : &register_exception_translator;
@ -2581,10 +2650,10 @@ register_exception_impl(handle scope, const char *name, handle base, bool isLoca
try {
std::rethrow_exception(p);
} catch (const CppException &e) {
detail::get_exception_object<CppException>()(e.what());
set_error(exc_storage.get_stored(), e.what());
}
});
return ex;
return exc_storage.get_stored();
}
PYBIND11_NAMESPACE_END(detail)
@ -2703,7 +2772,11 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
if ((std::string) str(f_code->co_name) == name && f_code->co_argcount > 0) {
PyObject *locals = PyEval_GetLocals();
if (locals != nullptr) {
# if PY_VERSION_HEX >= 0x030b0000
PyObject *co_varnames = PyCode_GetVarnames(f_code);
# else
PyObject *co_varnames = PyObject_GetAttrString((PyObject *) f_code, "co_varnames");
# endif
PyObject *self_arg = PyTuple_GET_ITEM(co_varnames, 0);
Py_DECREF(co_varnames);
PyObject *self_caller = dict_getitem(locals, self_arg);

View File

@ -59,6 +59,7 @@ struct sequence_item;
struct list_item;
struct tuple_item;
} // namespace accessor_policies
// PLEASE KEEP handle_type_name SPECIALIZATIONS IN SYNC.
using obj_attr_accessor = accessor<accessor_policies::obj_attr>;
using str_attr_accessor = accessor<accessor_policies::str_attr>;
using item_accessor = accessor<accessor_policies::generic_item>;
@ -310,14 +311,14 @@ private:
"used for all translation units linked into a given pybind11 extension, otherwise "
"there will be ODR violations.",
function_name.c_str());
fflush(stderr);
if (Py_TYPE(m_ptr)->tp_name != nullptr) {
fprintf(stderr,
"The failing %s call was triggered on a %s object.\n",
" The failing %s call was triggered on a %s object.",
function_name.c_str(),
Py_TYPE(m_ptr)->tp_name);
fflush(stderr);
}
fprintf(stderr, "\n");
fflush(stderr);
throw std::runtime_error(function_name + " PyGILState_Check() failure.");
}
#endif
@ -334,6 +335,14 @@ public:
#endif
};
inline void set_error(const handle &type, const char *message) {
PyErr_SetString(type.ptr(), message);
}
inline void set_error(const handle &type, const handle &value) {
PyErr_SetObject(type.ptr(), value.ptr());
}
/** \rst
Holds a reference to a Python object (with reference counting)
@ -1612,7 +1621,15 @@ inline namespace literals {
/** \rst
String literal version of `str`
\endrst */
inline str operator"" _s(const char *s, size_t size) { return {s, size}; }
inline str
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
operator"" _s // gcc 4.8.5 insists on having a space (hard error).
#else
operator""_s // clang 17 generates a deprecation warning if there is a space.
#endif
(const char *s, size_t size) {
return {s, size};
}
} // namespace literals
/// \addtogroup pytypes

View File

@ -100,7 +100,7 @@ public:
return s.release();
}
PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]"));
PYBIND11_TYPE_CASTER(type, const_name("set[") + key_conv::name + const_name("]"));
};
template <typename Type, typename Key, typename Value>
@ -157,7 +157,7 @@ public:
}
PYBIND11_TYPE_CASTER(Type,
const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name
const_name("dict[") + key_conv::name + const_name(", ") + value_conv::name
+ const_name("]"));
};
@ -172,7 +172,7 @@ struct list_caster {
auto s = reinterpret_borrow<sequence>(src);
value.clear();
reserve_maybe(s, &value);
for (auto it : s) {
for (const auto &it : s) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
@ -208,7 +208,7 @@ public:
return l.release();
}
PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_name("]"));
PYBIND11_TYPE_CASTER(Type, const_name("list[") + value_conv::name + const_name("]"));
};
template <typename Type, typename Alloc>
@ -247,7 +247,7 @@ public:
return false;
}
size_t ctr = 0;
for (auto it : l) {
for (const auto &it : l) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
@ -274,7 +274,7 @@ public:
PYBIND11_TYPE_CASTER(ArrayType,
const_name<Resizable>(const_name(""), const_name("Annotated["))
+ const_name("List[") + value_conv::name + const_name("]")
+ const_name("list[") + value_conv::name + const_name("]")
+ const_name<Resizable>(const_name(""),
const_name(", FixedSize(")
+ const_name<Size>() + const_name(")]")));
@ -421,7 +421,8 @@ struct variant_caster<V<Ts...>> {
using Type = V<Ts...>;
PYBIND11_TYPE_CASTER(Type,
const_name("Union[") + detail::concat(make_caster<Ts>::name...)
const_name("Union[")
+ ::pybind11::detail::concat(make_caster<Ts>::name...)
+ const_name("]"));
};

View File

@ -525,7 +525,7 @@ class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, A
[](const Vector &v) -> bool { return !v.empty(); },
"Check whether the list is nonempty");
cl.def("__len__", &Vector::size);
cl.def("__len__", [](const Vector &vec) { return vec.size(); });
#if 0
// C++ style functions deprecated, leaving it here as an example
@ -645,49 +645,50 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name)
"Return the canonical string representation of this map.");
}
template <typename KeyType>
struct keys_view {
virtual size_t len() = 0;
virtual iterator iter() = 0;
virtual bool contains(const KeyType &k) = 0;
virtual bool contains(const object &k) = 0;
virtual bool contains(const handle &k) = 0;
virtual ~keys_view() = default;
};
template <typename MappedType>
struct values_view {
virtual size_t len() = 0;
virtual iterator iter() = 0;
virtual ~values_view() = default;
};
template <typename KeyType, typename MappedType>
struct items_view {
virtual size_t len() = 0;
virtual iterator iter() = 0;
virtual ~items_view() = default;
};
template <typename Map, typename KeysView>
struct KeysViewImpl : public KeysView {
template <typename Map>
struct KeysViewImpl : public detail::keys_view {
explicit KeysViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); }
iterator iter() override { return make_key_iterator(map.begin(), map.end()); }
bool contains(const typename Map::key_type &k) override { return map.find(k) != map.end(); }
bool contains(const object &) override { return false; }
bool contains(const handle &k) override {
try {
return map.find(k.template cast<typename Map::key_type>()) != map.end();
} catch (const cast_error &) {
return false;
}
}
Map &map;
};
template <typename Map, typename ValuesView>
struct ValuesViewImpl : public ValuesView {
template <typename Map>
struct ValuesViewImpl : public detail::values_view {
explicit ValuesViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); }
iterator iter() override { return make_value_iterator(map.begin(), map.end()); }
Map &map;
};
template <typename Map, typename ItemsView>
struct ItemsViewImpl : public ItemsView {
template <typename Map>
struct ItemsViewImpl : public detail::items_view {
explicit ItemsViewImpl(Map &map) : map(map) {}
size_t len() override { return map.size(); }
iterator iter() override { return make_iterator(map.begin(), map.end()); }
@ -700,11 +701,9 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) {
using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type;
using StrippedKeyType = detail::remove_cvref_t<KeyType>;
using StrippedMappedType = detail::remove_cvref_t<MappedType>;
using KeysView = detail::keys_view<StrippedKeyType>;
using ValuesView = detail::values_view<StrippedMappedType>;
using ItemsView = detail::items_view<StrippedKeyType, StrippedMappedType>;
using KeysView = detail::keys_view;
using ValuesView = detail::values_view;
using ItemsView = detail::items_view;
using Class_ = class_<Map, holder_type>;
// If either type is a non-module-local bound type then make the map binding non-local as well;
@ -718,39 +717,20 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
}
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
static constexpr auto key_type_descr = detail::make_caster<KeyType>::name;
static constexpr auto mapped_type_descr = detail::make_caster<MappedType>::name;
std::string key_type_name(key_type_descr.text), mapped_type_name(mapped_type_descr.text);
// If key type isn't properly wrapped, fall back to C++ names
if (key_type_name == "%") {
key_type_name = detail::type_info_description(typeid(KeyType));
}
// Similarly for value type:
if (mapped_type_name == "%") {
mapped_type_name = detail::type_info_description(typeid(MappedType));
}
// Wrap KeysView[KeyType] if it wasn't already wrapped
// Wrap KeysView if it wasn't already wrapped
if (!detail::get_type_info(typeid(KeysView))) {
class_<KeysView> keys_view(
scope, ("KeysView[" + key_type_name + "]").c_str(), pybind11::module_local(local));
class_<KeysView> keys_view(scope, "KeysView", pybind11::module_local(local));
keys_view.def("__len__", &KeysView::len);
keys_view.def("__iter__",
&KeysView::iter,
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
keys_view.def("__contains__",
static_cast<bool (KeysView::*)(const KeyType &)>(&KeysView::contains));
// Fallback for when the object is not of the key type
keys_view.def("__contains__",
static_cast<bool (KeysView::*)(const object &)>(&KeysView::contains));
keys_view.def("__contains__", &KeysView::contains);
}
// Similarly for ValuesView:
if (!detail::get_type_info(typeid(ValuesView))) {
class_<ValuesView> values_view(scope,
("ValuesView[" + mapped_type_name + "]").c_str(),
pybind11::module_local(local));
class_<ValuesView> values_view(scope, "ValuesView", pybind11::module_local(local));
values_view.def("__len__", &ValuesView::len);
values_view.def("__iter__",
&ValuesView::iter,
@ -759,10 +739,7 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
}
// Similarly for ItemsView:
if (!detail::get_type_info(typeid(ItemsView))) {
class_<ItemsView> items_view(
scope,
("ItemsView[" + key_type_name + ", ").append(mapped_type_name + "]").c_str(),
pybind11::module_local(local));
class_<ItemsView> items_view(scope, "ItemsView", pybind11::module_local(local));
items_view.def("__len__", &ItemsView::len);
items_view.def("__iter__",
&ItemsView::iter,
@ -788,25 +765,19 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
cl.def(
"keys",
[](Map &m) {
return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map, KeysView>(m));
},
[](Map &m) { return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map>(m)); },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
cl.def(
"values",
[](Map &m) {
return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map, ValuesView>(m));
},
[](Map &m) { return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map>(m)); },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
cl.def(
"items",
[](Map &m) {
return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map, ItemsView>(m));
},
[](Map &m) { return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map>(m)); },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
@ -843,7 +814,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
m.erase(it);
});
cl.def("__len__", &Map::size);
// Always use a lambda in case of `using` declaration
cl.def("__len__", [](const Map &m) { return m.size(); });
return cl;
}

125
include/pybind11/typing.h Normal file
View File

@ -0,0 +1,125 @@
/*
pybind11/typing.h: Convenience wrapper classes for basic Python types
with more explicit annotations.
Copyright (c) 2023 Dustin Spicuzza <dustin@virtualroadside.com>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include "detail/common.h"
#include "cast.h"
#include "pytypes.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(typing)
/*
The following types can be used to direct pybind11-generated docstrings
to have have more explicit types (e.g., `list[str]` instead of `list`).
Just use these in place of existing types.
There is no additional enforcement of types at runtime.
*/
template <typename... Types>
class Tuple : public tuple {
using tuple::tuple;
};
template <typename K, typename V>
class Dict : public dict {
using dict::dict;
};
template <typename T>
class List : public list {
using list::list;
};
template <typename T>
class Set : public set {
using set::set;
};
template <typename T>
class Iterable : public iterable {
using iterable::iterable;
};
template <typename T>
class Iterator : public iterator {
using iterator::iterator;
};
template <typename Signature>
class Callable;
template <typename Return, typename... Args>
class Callable<Return(Args...)> : public function {
using function::function;
};
PYBIND11_NAMESPACE_END(typing)
PYBIND11_NAMESPACE_BEGIN(detail)
template <typename... Types>
struct handle_type_name<typing::Tuple<Types...>> {
static constexpr auto name = const_name("tuple[")
+ ::pybind11::detail::concat(make_caster<Types>::name...)
+ const_name("]");
};
template <>
struct handle_type_name<typing::Tuple<>> {
// PEP 484 specifies this syntax for an empty tuple
static constexpr auto name = const_name("tuple[()]");
};
template <typename T>
struct handle_type_name<typing::Tuple<T, ellipsis>> {
// PEP 484 specifies this syntax for a variable-length tuple
static constexpr auto name
= const_name("tuple[") + make_caster<T>::name + const_name(", ...]");
};
template <typename K, typename V>
struct handle_type_name<typing::Dict<K, V>> {
static constexpr auto name = const_name("dict[") + make_caster<K>::name + const_name(", ")
+ make_caster<V>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::List<T>> {
static constexpr auto name = const_name("list[") + make_caster<T>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::Set<T>> {
static constexpr auto name = const_name("set[") + make_caster<T>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::Iterable<T>> {
static constexpr auto name = const_name("Iterable[") + make_caster<T>::name + const_name("]");
};
template <typename T>
struct handle_type_name<typing::Iterator<T>> {
static constexpr auto name = const_name("Iterator[") + make_caster<T>::name + const_name("]");
};
template <typename Return, typename... Args>
struct handle_type_name<typing::Callable<Return(Args...)>> {
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
static constexpr auto name
= const_name("Callable[[") + ::pybind11::detail::concat(make_caster<Args>::name...)
+ const_name("], ") + make_caster<retval_type>::name + const_name("]");
};
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -57,7 +57,7 @@ def tests_packaging(session: nox.Session) -> None:
Run the packaging tests.
"""
session.install("-r", "tests/requirements.txt", "--prefer-binary")
session.install("-r", "tests/requirements.txt")
session.run("pytest", "tests/extra_python_package", *session.posargs)

View File

@ -8,5 +8,5 @@ def _to_int(s: str) -> Union[int, str]:
return s
__version__ = "2.11.1"
__version__ = "2.12.0"
version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -66,7 +66,9 @@ try:
from setuptools import Extension as _Extension
from setuptools.command.build_ext import build_ext as _build_ext
except ImportError:
from distutils.command.build_ext import build_ext as _build_ext # type: ignore[assignment]
from distutils.command.build_ext import ( # type: ignore[assignment]
build_ext as _build_ext,
)
from distutils.extension import Extension as _Extension # type: ignore[assignment]
import distutils.ccompiler

View File

@ -19,7 +19,7 @@ ignore = [
[tool.mypy]
files = ["pybind11"]
python_version = "3.6"
python_version = "3.7"
strict = true
show_error_codes = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
@ -57,10 +57,12 @@ messages_control.disable = [
"unused-argument", # covered by Ruff ARG
]
[tool.ruff]
select = [
"E", "F", "W", # flake8
target-version = "py37"
src = ["src"]
[tool.ruff.lint]
extend-select = [
"B", # flake8-bugbear
"I", # isort
"N", # pep8-naming
@ -68,7 +70,6 @@ select = [
"C4", # flake8-comprehensions
"EM", # flake8-errmsg
"ICN", # flake8-import-conventions
"ISC", # flake8-implicit-str-concat
"PGH", # pygrep-hooks
"PIE", # flake8-pie
"PL", # pylint
@ -86,13 +87,9 @@ ignore = [
"PT004", # Fixture that doesn't return needs underscore (no, it is fine)
"SIM118", # iter(x) is not always the same as iter(x.keys())
]
target-version = "py37"
src = ["src"]
unfixable = ["T20"]
exclude = []
line-length = 120
isort.known-first-party = ["env", "pybind11_cross_module_tests", "pybind11_tests"]
[tool.ruff.per-file-ignores]
"tests/**" = ["EM", "N"]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["EM", "N", "E721"]
"tests/test_call_policies.py" = ["PLC1901"]

View File

@ -7,13 +7,13 @@
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
# Filter out items; print an optional message if any items filtered. This ignores extensions.
@ -144,6 +144,7 @@ set(PYBIND11_TEST_FILES
test_opaque_types
test_operator_overloading
test_pickling
test_python_multiple_inheritance
test_pytypes
test_sequences_and_iterators
test_smart_ptr
@ -519,11 +520,15 @@ set(PYBIND11_TEST_PREFIX_COMMAND
""
CACHE STRING "Put this before pytest, use for checkers and such")
set(PYBIND11_PYTEST_ARGS
""
CACHE STRING "Extra arguments for pytest")
# A single command to compile and run the tests
add_custom_target(
pytest
COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest
${PYBIND11_ABS_PYTEST_FILES}
${PYBIND11_ABS_PYTEST_FILES} ${PYBIND11_PYTEST_ARGS}
DEPENDS ${test_targets}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
USES_TERMINAL)

View File

@ -218,4 +218,5 @@ def pytest_report_header(config):
f" {pybind11_tests.cpp_std}"
f" {pybind11_tests.PYBIND11_INTERNALS_ID}"
f" PYBIND11_SIMPLE_GIL_MANAGEMENT={pybind11_tests.PYBIND11_SIMPLE_GIL_MANAGEMENT}"
f" PYBIND11_NUMPY_1_ONLY={pybind11_tests.PYBIND11_NUMPY_1_ONLY}"
)

View File

@ -16,12 +16,12 @@ namespace {
namespace py = pybind11;
void interleaved_error_already_set() {
PyErr_SetString(PyExc_RuntimeError, "1st error.");
py::set_error(PyExc_RuntimeError, "1st error.");
try {
throw py::error_already_set();
} catch (const py::error_already_set &) {
// The 2nd error could be conditional in a real application.
PyErr_SetString(PyExc_RuntimeError, "2nd error.");
py::set_error(PyExc_RuntimeError, "2nd error.");
} // Here the 1st error is destroyed before the 2nd error is fetched.
// The error_already_set dtor triggers a pybind11::detail::get_internals()
// call via pybind11::gil_scoped_acquire.

View File

@ -35,6 +35,7 @@ main_headers = {
"include/pybind11/eval.h",
"include/pybind11/functional.h",
"include/pybind11/gil.h",
"include/pybind11/gil_safe_call_once.h",
"include/pybind11/iostream.h",
"include/pybind11/numpy.h",
"include/pybind11/operators.h",
@ -44,6 +45,7 @@ main_headers = {
"include/pybind11/stl.h",
"include/pybind11/stl_bind.h",
"include/pybind11/type_caster_pyobject_ptr.h",
"include/pybind11/typing.h",
}
detail_headers = {

View File

@ -31,11 +31,11 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
// test_exceptions.py
py::register_local_exception<LocalSimpleException>(m, "LocalSimpleException");
m.def("raise_runtime_error", []() {
PyErr_SetString(PyExc_RuntimeError, "My runtime error");
py::set_error(PyExc_RuntimeError, "My runtime error");
throw py::error_already_set();
});
m.def("raise_value_error", []() {
PyErr_SetString(PyExc_ValueError, "My value error");
py::set_error(PyExc_ValueError, "My value error");
throw py::error_already_set();
});
m.def("throw_pybind_value_error", []() { throw py::value_error("pybind11 value error"); });
@ -49,7 +49,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
std::rethrow_exception(p);
}
} catch (const shared_exception &e) {
PyErr_SetString(PyExc_KeyError, e.what());
py::set_error(PyExc_KeyError, e.what());
}
});
@ -60,7 +60,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
std::rethrow_exception(p);
}
} catch (const LocalException &e) {
PyErr_SetString(PyExc_KeyError, e.what());
py::set_error(PyExc_KeyError, e.what());
}
});

View File

@ -80,10 +80,10 @@ PYBIND11_MODULE(pybind11_tests, m) {
// Intentionally kept minimal to not create a maintenance chore
// ("just enough" to be conclusive).
#if defined(_MSC_FULL_VER)
m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER);
#elif defined(__VERSION__)
#if defined(__VERSION__)
m.attr("compiler_info") = __VERSION__;
#elif defined(_MSC_FULL_VER)
m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER);
#else
m.attr("compiler_info") = py::none();
#endif
@ -95,6 +95,12 @@ PYBIND11_MODULE(pybind11_tests, m) {
#else
false;
#endif
m.attr("PYBIND11_NUMPY_1_ONLY") =
#if defined(PYBIND11_NUMPY_1_ONLY)
true;
#else
false;
#endif
bind_ConstructorStats(m);

View File

@ -1,9 +1,15 @@
build==0.8.0
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
pytest==7.0.0
--only-binary=:all:
build~=0.9; python_version=="3.6"
build~=1.0; python_version>="3.7"
numpy~=1.20.0; python_version=="3.7" and platform_python_implementation=="PyPy"
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~=1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy~=1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
numpy~=1.22.2; platform_python_implementation!="PyPy" and python_version=="3.10"
numpy~=1.26.0; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13"
pytest~=7.0
pytest-timeout
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
scipy==1.10.0; platform_python_implementation!="PyPy" and python_version=="3.10"
scipy~=1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
scipy~=1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10"
scipy~=1.11.1; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13"

View File

@ -219,3 +219,10 @@ def test_ctypes_from_buffer():
assert cinfo.shape == pyinfo.shape
assert cinfo.strides == pyinfo.strides
assert not cinfo.readonly
def test_buffer_docstring():
assert (
m.get_buffer_info.__doc__.strip()
== "get_buffer_info(arg0: Buffer) -> pybind11_tests.buffers.buffer_info"
)

View File

@ -352,7 +352,7 @@ def test_tuple(doc):
assert (
doc(m.pair_passthrough)
== """
pair_passthrough(arg0: Tuple[bool, str]) -> Tuple[str, bool]
pair_passthrough(arg0: tuple[bool, str]) -> tuple[str, bool]
Return a pair in reversed order
"""
@ -360,7 +360,7 @@ def test_tuple(doc):
assert (
doc(m.tuple_passthrough)
== """
tuple_passthrough(arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool]
tuple_passthrough(arg0: tuple[bool, str, int]) -> tuple[int, str, bool]
Return a triple in reversed order
"""

View File

@ -216,3 +216,10 @@ def test_custom_func():
def test_custom_func2():
assert m.custom_function2(3) == 27
assert m.roundtrip(m.custom_function2)(3) == 27
def test_callback_docstring():
assert (
m.test_tuple_unpacking.__doc__.strip()
== "test_tuple_unpacking(arg0: Callable) -> object"
)

View File

@ -1,3 +1,5 @@
from unittest import mock
import pytest
import env
@ -203,6 +205,18 @@ def test_inheritance_init(msg):
assert msg(exc_info.value) == expected
@pytest.mark.parametrize(
"mock_return_value", [None, (1, 2, 3), m.Pet("Polly", "parrot"), m.Dog("Molly")]
)
def test_mock_new(mock_return_value):
with mock.patch.object(
m.Pet, "__new__", return_value=mock_return_value
) as mock_new:
obj = m.Pet("Noname", "Nospecies")
assert obj is mock_return_value
mock_new.assert_called_once_with(m.Pet, "Noname", "Nospecies")
def test_automatic_upcasting():
assert type(m.return_class_1()).__name__ == "DerivedClass1"
assert type(m.return_class_2()).__name__ == "DerivedClass2"

View File

@ -5,9 +5,8 @@ function(pybind11_add_build_test name)
set(build_options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
if(PYBIND11_FINDPYTHON)
list(APPEND build_options "-DPYBIND11_FINDPYTHON=${PYBIND11_FINDPYTHON}")
if(PYBIND11_FINDPYTHON)
if(DEFINED Python_ROOT_DIR)
list(APPEND build_options "-DPython_ROOT_DIR=${Python_ROOT_DIR}")
endif()

View File

@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
project(test_installed_embed CXX)

View File

@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.5)
project(test_installed_module CXX)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
project(test_installed_function CXX)

View File

@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
project(test_installed_target CXX)

View File

@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
project(test_subdirectory_embed CXX)
@ -16,6 +16,12 @@ set(PYBIND11_INSTALL
CACHE BOOL "")
set(PYBIND11_EXPORT_NAME test_export)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes).
if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE)
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif()
add_subdirectory("${pybind11_SOURCE_DIR}" pybind11)
# Test basic target functionality

View File

@ -1,16 +1,22 @@
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
project(test_subdirectory_function CXX)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes).
if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE)
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif()
add_subdirectory("${pybind11_SOURCE_DIR}" pybind11)
pybind11_add_module(test_subdirectory_function ../main.cpp)
set_target_properties(test_subdirectory_function PROPERTIES OUTPUT_NAME test_cmake_build)

View File

@ -1,16 +1,22 @@
cmake_minimum_required(VERSION 3.5)
# The `cmake_minimum_required(VERSION 3.5...3.26)` syntax does not work with
# The `cmake_minimum_required(VERSION 3.5...3.27)` syntax does not work with
# some versions of VS that have a patched CMake 3.11. This forces us to emulate
# the behavior using the following workaround:
if(${CMAKE_VERSION} VERSION_LESS 3.26)
if(${CMAKE_VERSION} VERSION_LESS 3.27)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.26)
cmake_policy(VERSION 3.27)
endif()
project(test_subdirectory_target CXX)
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests
# (makes transition easier while we support both modes).
if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE)
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif()
add_subdirectory("${pybind11_SOURCE_DIR}" pybind11)
add_library(test_subdirectory_target MODULE ../main.cpp)

View File

@ -54,7 +54,11 @@ int f2(int x) noexcept(true) { return x + 2; }
int f3(int x) noexcept(false) { return x + 3; }
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
#if defined(__clang_major__) && __clang_major__ >= 5
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated-dynamic-exception-spec")
#else
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
#endif
// NOLINTNEXTLINE(modernize-use-noexcept)
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
PYBIND11_WARNING_POP

View File

@ -134,6 +134,16 @@ struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
} // namespace detail
} // namespace PYBIND11_NAMESPACE
// This simply is required to compile
namespace ADL_issue {
template <typename OutStringType = std::string, typename... Args>
OutStringType concat(Args &&...) {
return OutStringType();
}
struct test {};
} // namespace ADL_issue
TEST_SUBMODULE(custom_type_casters, m) {
// test_custom_type_casters
@ -206,4 +216,6 @@ TEST_SUBMODULE(custom_type_casters, m) {
py::return_value_policy::reference);
m.def("other_lib_type", [](other_lib::MyType x) { return x; });
m.def("_adl_issue", [](const ADL_issue::test &) {});
}

View File

@ -330,6 +330,23 @@ TEST_SUBMODULE(eigen_matrix, m) {
m.def("dense_c", [mat]() -> DenseMatrixC { return DenseMatrixC(mat); });
m.def("dense_copy_r", [](const DenseMatrixR &m) -> DenseMatrixR { return m; });
m.def("dense_copy_c", [](const DenseMatrixC &m) -> DenseMatrixC { return m; });
// test_defaults
bool have_numpy = true;
try {
py::module_::import("numpy");
} catch (const py::error_already_set &) {
have_numpy = false;
}
if (have_numpy) {
py::module_::import("numpy");
Eigen::Matrix<double, 3, 3> defaultMatrix = Eigen::Matrix3d::Identity();
m.def(
"defaults_mat", [](const Eigen::Matrix3d &) {}, py::arg("mat") = defaultMatrix);
Eigen::VectorXd defaultVector = Eigen::VectorXd::Ones(32);
m.def(
"defaults_vec", [](const Eigen::VectorXd &) {}, py::arg("vec") = defaultMatrix);
}
// test_sparse, test_sparse_signature
m.def("sparse_r", [mat]() -> SparseMatrixR {
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)

View File

@ -608,7 +608,9 @@ def test_both_ref_mutators():
def test_nocopy_wrapper():
# get_elem requires a column-contiguous matrix reference, but should be
# callable with other types of matrix (via copying):
int_matrix_colmajor = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], order="F")
int_matrix_colmajor = np.array(
[[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype="l", order="F"
)
dbl_matrix_colmajor = np.array(
int_matrix_colmajor, dtype="double", order="F", copy=True
)
@ -716,6 +718,11 @@ def test_dense_signature(doc):
)
def test_defaults(doc):
assert "\n" not in str(doc(m.defaults_mat))
assert "\n" not in str(doc(m.defaults_vec))
def test_named_arguments():
a = np.array([[1.0, 2], [3, 4], [5, 6]])
b = np.ones((2, 1))

View File

@ -60,9 +60,7 @@ Members:
ETwo : Docstring for ETwo
EThree : Docstring for EThree""".split(
"\n"
):
EThree : Docstring for EThree""".split("\n"):
assert docstring_line in m.UnscopedEnum.__doc__
# Unscoped enums will accept ==/!= int comparisons
@ -264,3 +262,8 @@ def test_docstring_signatures():
for attr in enum_type.__dict__.values():
# Issue #2623/PR #2637: Add argument names to enum_ methods
assert "arg0" not in (attr.__doc__ or "")
def test_str_signature():
for enum_type in [m.ScopedEnum, m.UnscopedEnum]:
assert enum_type.__str__.__doc__.startswith("__str__")

View File

@ -6,6 +6,8 @@
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/gil_safe_call_once.h>
#include "test_exceptions.h"
#include "local_bindings.h"
@ -25,6 +27,10 @@ private:
std::string message = "";
};
class MyExceptionUseDeprecatedOperatorCall : public MyException {
using MyException::MyException;
};
// A type that should be translated to a standard Python exception
class MyException2 : public std::exception {
public:
@ -109,8 +115,10 @@ TEST_SUBMODULE(exceptions, m) {
m.def("throw_std_exception",
[]() { throw std::runtime_error("This exception was intentionally thrown."); });
// make a new custom exception and use it as a translation target
static py::exception<MyException> ex(m, "MyException");
// PLEASE KEEP IN SYNC with docs/advanced/exceptions.rst
PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> ex_storage;
ex_storage.call_once_and_store_result(
[&]() { return py::exception<MyException>(m, "MyException"); });
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) {
@ -118,7 +126,32 @@ TEST_SUBMODULE(exceptions, m) {
}
} catch (const MyException &e) {
// Set MyException as the active python error
ex(e.what());
py::set_error(ex_storage.get_stored(), e.what());
}
});
// Same as above, but using the deprecated `py::exception<>::operator()`
// We want to be sure it still works, until it's removed.
static const auto *const exd = new py::exception<MyExceptionUseDeprecatedOperatorCall>(
m, "MyExceptionUseDeprecatedOperatorCall");
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) {
std::rethrow_exception(p);
}
} catch (const MyExceptionUseDeprecatedOperatorCall &e) {
#if defined(__INTEL_COMPILER) || defined(__NVCOMPILER)
// It is not worth the trouble dealing with warning suppressions for these compilers.
// Falling back to the recommended approach to keep the test code simple.
py::set_error(*exd, e.what());
#else
PYBIND11_WARNING_PUSH
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
PYBIND11_WARNING_DISABLE_MSVC(4996)
(*exd)(e.what());
PYBIND11_WARNING_POP
#endif
}
});
@ -132,7 +165,7 @@ TEST_SUBMODULE(exceptions, m) {
}
} catch (const MyException2 &e) {
// Translate this exception to a standard RuntimeError
PyErr_SetString(PyExc_RuntimeError, e.what());
py::set_error(PyExc_RuntimeError, e.what());
}
});
@ -162,11 +195,16 @@ TEST_SUBMODULE(exceptions, m) {
std::rethrow_exception(p);
}
} catch (const MyException6 &e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
py::set_error(PyExc_RuntimeError, e.what());
}
});
m.def("throws1", []() { throw MyException("this error should go to a custom type"); });
m.def("throws1",
[]() { throw MyException("this error should go to py::exception<MyException>"); });
m.def("throws1d", []() {
throw MyExceptionUseDeprecatedOperatorCall(
"this error should go to py::exception<MyExceptionUseDeprecatedOperatorCall>");
});
m.def("throws2",
[]() { throw MyException2("this error should go to a standard Python exception"); });
m.def("throws3", []() { throw MyException3("this error cannot be translated"); });
@ -222,7 +260,7 @@ TEST_SUBMODULE(exceptions, m) {
m.def("throw_already_set", [](bool err) {
if (err) {
PyErr_SetString(PyExc_ValueError, "foo");
py::set_error(PyExc_ValueError, "foo");
}
try {
throw py::error_already_set();
@ -238,7 +276,7 @@ TEST_SUBMODULE(exceptions, m) {
}
PyErr_Clear();
if (err) {
PyErr_SetString(PyExc_ValueError, "foo");
py::set_error(PyExc_ValueError, "foo");
}
throw py::error_already_set();
});
@ -247,7 +285,7 @@ TEST_SUBMODULE(exceptions, m) {
bool retval = false;
try {
PythonCallInDestructor set_dict_in_destructor(d);
PyErr_SetString(PyExc_ValueError, "foo");
py::set_error(PyExc_ValueError, "foo");
throw py::error_already_set();
} catch (const py::error_already_set &) {
retval = true;
@ -282,14 +320,14 @@ TEST_SUBMODULE(exceptions, m) {
m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
m.def("raise_from", []() {
PyErr_SetString(PyExc_ValueError, "inner");
py::set_error(PyExc_ValueError, "inner");
py::raise_from(PyExc_ValueError, "outer");
throw py::error_already_set();
});
m.def("raise_from_already_set", []() {
try {
PyErr_SetString(PyExc_ValueError, "inner");
py::set_error(PyExc_ValueError, "inner");
throw py::error_already_set();
} catch (py::error_already_set &e) {
py::raise_from(e, PyExc_ValueError, "outer");
@ -306,7 +344,7 @@ TEST_SUBMODULE(exceptions, m) {
});
m.def("error_already_set_what", [](const py::object &exc_type, const py::object &exc_value) {
PyErr_SetObject(exc_type.ptr(), exc_value.ptr());
py::set_error(exc_type, exc_value);
std::string what = py::error_already_set().what();
bool py_err_set_after_what = (PyErr_Occurred() != nullptr);
PyErr_Clear();
@ -321,7 +359,7 @@ TEST_SUBMODULE(exceptions, m) {
});
m.def("test_error_already_set_double_restore", [](bool dry_run) {
PyErr_SetString(PyExc_ValueError, "Random error.");
py::set_error(PyExc_ValueError, "Random error.");
py::error_already_set e;
e.restore();
PyErr_Clear();
@ -344,4 +382,7 @@ TEST_SUBMODULE(exceptions, m) {
// function returns None instead of int, should give a useful error message
fn().cast<int>();
});
// m.def("pass_exception_void", [](const py::exception<void>&) {}); // Does not compile.
m.def("return_exception_void", []() { return py::exception<void>(); });
}

View File

@ -9,5 +9,5 @@ class PYBIND11_EXPORT_EXCEPTION shared_exception : public pybind11::builtin_exce
public:
using builtin_exception::builtin_exception;
explicit shared_exception() : shared_exception("") {}
void set_error() const override { PyErr_SetString(PyExc_RuntimeError, what()); }
void set_error() const override { py::set_error(PyExc_RuntimeError, what()); }
};

View File

@ -4,7 +4,7 @@ import pytest
import env
import pybind11_cross_module_tests as cm
import pybind11_tests # noqa: F401
import pybind11_tests
from pybind11_tests import exceptions as m
@ -139,7 +139,15 @@ def test_custom(msg):
# Can we catch a MyException?
with pytest.raises(m.MyException) as excinfo:
m.throws1()
assert msg(excinfo.value) == "this error should go to a custom type"
assert msg(excinfo.value) == "this error should go to py::exception<MyException>"
# Can we catch a MyExceptionUseDeprecatedOperatorCall?
with pytest.raises(m.MyExceptionUseDeprecatedOperatorCall) as excinfo:
m.throws1d()
assert (
msg(excinfo.value)
== "this error should go to py::exception<MyExceptionUseDeprecatedOperatorCall>"
)
# Can we translate to standard Python exceptions?
with pytest.raises(RuntimeError) as excinfo:
@ -240,6 +248,11 @@ def test_nested_throws(capture):
assert str(excinfo.value) == "this is a helper-defined translated exception"
# TODO: Investigate this crash, see pybind/pybind11#5062 for background
@pytest.mark.skipif(
sys.platform.startswith("win32") and "Clang" in pybind11_tests.compiler_info,
reason="Started segfaulting February 2024",
)
def test_throw_nested_exception():
with pytest.raises(RuntimeError) as excinfo:
m.throw_nested_exception()
@ -411,3 +424,9 @@ def test_fn_cast_int_exception():
assert str(excinfo.value).startswith(
"Unable to cast Python instance of type <class 'NoneType'> to C++ type"
)
def test_return_exception_void():
with pytest.raises(TypeError) as excinfo:
m.return_exception_void()
assert "Exception" in str(excinfo.value)

View File

@ -77,7 +77,7 @@ def test_init_factory_signature(msg):
1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int)
2. m.factory_constructors.TestFactory1(arg0: str)
3. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.pointer_tag)
4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle)
4. m.factory_constructors.TestFactory1(arg0: object, arg1: int, arg2: object)
Invoked with: 'invalid', 'constructor', 'arguments'
"""
@ -95,7 +95,7 @@ def test_init_factory_signature(msg):
3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
4. __init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None
4. __init__(self: m.factory_constructors.TestFactory1, arg0: object, arg1: int, arg2: object) -> None
"""
)

View File

@ -42,6 +42,52 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
m.def("kw_func_udl", kw_func, "x"_a, "y"_a = 300);
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a = 0);
// test line breaks in default argument representation
struct CustomRepr {
std::string repr_string;
explicit CustomRepr(const std::string &repr) : repr_string(repr) {}
std::string __repr__() const { return repr_string; }
};
py::class_<CustomRepr>(m, "CustomRepr")
.def(py::init<const std::string &>())
.def("__repr__", &CustomRepr::__repr__);
m.def(
"kw_lb_func0",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr(" array([[A, B], [C, D]]) "));
m.def(
"kw_lb_func1",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr(" array([[A, B],\n[C, D]]) "));
m.def(
"kw_lb_func2",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("\v\n array([[A, B], [C, D]])"));
m.def(
"kw_lb_func3",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("array([[A, B], [C, D]]) \f\n"));
m.def(
"kw_lb_func4",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("array([[A, B],\n\f\n[C, D]])"));
m.def(
"kw_lb_func5",
[](const CustomRepr &) {},
py::arg("custom") = CustomRepr("array([[A, B],\r [C, D]])"));
m.def(
"kw_lb_func6", [](const CustomRepr &) {}, py::arg("custom") = CustomRepr(" \v\t "));
m.def(
"kw_lb_func7",
[](const std::string &) {},
py::arg("str_arg") = "First line.\n Second line.");
m.def(
"kw_lb_func8", [](const CustomRepr &) {}, py::arg("custom") = CustomRepr(""));
// test_args_and_kwargs
m.def("args_function", [](py::args args) -> py::tuple {
PYBIND11_WARNING_PUSH

View File

@ -8,7 +8,7 @@ def test_function_signatures(doc):
assert doc(m.kw_func1) == "kw_func1(x: int, y: int) -> str"
assert doc(m.kw_func2) == "kw_func2(x: int = 100, y: int = 200) -> str"
assert doc(m.kw_func3) == "kw_func3(data: str = 'Hello world!') -> None"
assert doc(m.kw_func4) == "kw_func4(myList: List[int] = [13, 17]) -> str"
assert doc(m.kw_func4) == "kw_func4(myList: list[int] = [13, 17]) -> str"
assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int = 300) -> str"
assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int = 0) -> str"
assert doc(m.args_function) == "args_function(*args) -> tuple"
@ -23,6 +23,42 @@ def test_function_signatures(doc):
doc(m.KWClass.foo1)
== "foo1(self: m.kwargs_and_defaults.KWClass, x: int, y: float) -> None"
)
assert (
doc(m.kw_lb_func0)
== "kw_lb_func0(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func1)
== "kw_lb_func1(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func2)
== "kw_lb_func2(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func3)
== "kw_lb_func3(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func4)
== "kw_lb_func4(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func5)
== "kw_lb_func5(custom: m.kwargs_and_defaults.CustomRepr = array([[A, B], [C, D]])) -> None"
)
assert (
doc(m.kw_lb_func6)
== "kw_lb_func6(custom: m.kwargs_and_defaults.CustomRepr = ) -> None"
)
assert (
doc(m.kw_lb_func7)
== "kw_lb_func7(str_arg: str = 'First line.\\n Second line.') -> None"
)
assert (
doc(m.kw_lb_func8)
== "kw_lb_func8(custom: m.kwargs_and_defaults.CustomRepr = ) -> None"
)
def test_named_arguments():

View File

@ -232,26 +232,30 @@ def test_no_mixed_overloads():
with pytest.raises(RuntimeError) as excinfo:
m.ExampleMandA.add_mixed_overloads1()
assert str(
excinfo.value
) == "overloading a method with both static and instance methods is not supported; " + (
assert (
str(excinfo.value)
== "overloading a method with both static and instance methods is not supported; "
+ (
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details"
if not detailed_error_messages_enabled
else "error while attempting to bind static method ExampleMandA.overload_mixed1"
"(arg0: float) -> str"
)
)
with pytest.raises(RuntimeError) as excinfo:
m.ExampleMandA.add_mixed_overloads2()
assert str(
excinfo.value
) == "overloading a method with both static and instance methods is not supported; " + (
assert (
str(excinfo.value)
== "overloading a method with both static and instance methods is not supported; "
+ (
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more details"
if not detailed_error_messages_enabled
else "error while attempting to bind instance method ExampleMandA.overload_mixed2"
"(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)"
" -> str"
)
)
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])

View File

@ -298,7 +298,7 @@ def test_constructors():
results = m.converting_constructors([1, 2, 3])
for a in results.values():
np.testing.assert_array_equal(a, [1, 2, 3])
assert results["array"].dtype == np.int_
assert results["array"].dtype == np.dtype(int)
assert results["array_t<int32>"].dtype == np.int32
assert results["array_t<double>"].dtype == np.float64
@ -536,7 +536,12 @@ def test_format_descriptors_for_floating_point_types(test_func):
@pytest.mark.parametrize("contiguity", [None, "C", "F"])
@pytest.mark.parametrize("noconvert", [False, True])
@pytest.mark.filterwarnings(
"ignore:Casting complex values to real discards the imaginary part:numpy.ComplexWarning"
"ignore:Casting complex values to real discards the imaginary part:"
+ (
"numpy.exceptions.ComplexWarning"
if hasattr(np, "exceptions")
else "numpy.ComplexWarning"
)
)
def test_argument_conversions(forcecast, contiguity, noconvert):
function_name = "accept_double"
@ -583,7 +588,8 @@ def test_argument_conversions(forcecast, contiguity, noconvert):
def test_dtype_refcount_leak():
from sys import getrefcount
dtype = np.dtype(np.float_)
# Was np.float_ but that alias for float64 was removed in NumPy 2.
dtype = np.dtype(np.float64)
a = np.array([1], dtype=dtype)
before = getrefcount(dtype)
m.ndim(a)

View File

@ -405,10 +405,35 @@ TEST_SUBMODULE(numpy_dtypes, m) {
});
// test_dtype
// Below we use `L` for unsigned long as unfortunately the only name that
// works reliably on Both NumPy 2.x and old NumPy 1.x.
std::vector<const char *> dtype_names{
"byte", "short", "intc", "int_", "longlong", "ubyte", "ushort",
"uintc", "uint", "ulonglong", "half", "single", "double", "longdouble",
"csingle", "cdouble", "clongdouble", "bool_", "datetime64", "timedelta64", "object_"};
"byte",
"short",
"intc",
"long",
"longlong",
"ubyte",
"ushort",
"uintc",
"L",
"ulonglong",
"half",
"single",
"double",
"longdouble",
"csingle",
"cdouble",
"clongdouble",
"bool_",
"datetime64",
"timedelta64",
"object_",
// platform dependent aliases (int_ and uint are also NumPy version dependent on windows)
"int_",
"uint",
"intp",
"uintp"};
m.def("print_dtypes", []() {
py::list l;

View File

@ -3,6 +3,7 @@ import re
import pytest
import env # noqa: F401
from pybind11_tests import PYBIND11_NUMPY_1_ONLY
from pybind11_tests import numpy_dtypes as m
np = pytest.importorskip("numpy")
@ -172,13 +173,20 @@ def test_dtype(simple_dtype):
np.zeros(1, m.trailing_padding_dtype())
)
expected_chars = "bhilqBHILQefdgFDG?MmO"
assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmO")
expected_chars = list("bhilqBHILQefdgFDG?MmO")
# Note that int_ and uint size and mapping is NumPy version dependent:
expected_chars += [np.dtype(_).char for _ in ("int_", "uint", "intp", "uintp")]
assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmOiuiu")
assert m.test_dtype_char_() == list(expected_chars)
assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars]
assert m.test_dtype_byteorder() == [np.dtype(ch).byteorder for ch in expected_chars]
assert m.test_dtype_alignment() == [np.dtype(ch).alignment for ch in expected_chars]
assert m.test_dtype_flags() == [chr(np.dtype(ch).flags) for ch in expected_chars]
if not PYBIND11_NUMPY_1_ONLY:
assert m.test_dtype_flags() == [np.dtype(ch).flags for ch in expected_chars]
else:
assert m.test_dtype_flags() == [
chr(np.dtype(ch).flags) for ch in expected_chars
]
def test_recarray(simple_dtype, packed_dtype):

View File

@ -0,0 +1,45 @@
#include "pybind11_tests.h"
namespace test_python_multiple_inheritance {
// Copied from:
// https://github.com/google/clif/blob/5718e4d0807fd3b6a8187dde140069120b81ecef/clif/testing/python_multiple_inheritance.h
struct CppBase {
explicit CppBase(int value) : base_value(value) {}
int get_base_value() const { return base_value; }
void reset_base_value(int new_value) { base_value = new_value; }
private:
int base_value;
};
struct CppDrvd : CppBase {
explicit CppDrvd(int value) : CppBase(value), drvd_value(value * 3) {}
int get_drvd_value() const { return drvd_value; }
void reset_drvd_value(int new_value) { drvd_value = new_value; }
int get_base_value_from_drvd() const { return get_base_value(); }
void reset_base_value_from_drvd(int new_value) { reset_base_value(new_value); }
private:
int drvd_value;
};
} // namespace test_python_multiple_inheritance
TEST_SUBMODULE(python_multiple_inheritance, m) {
using namespace test_python_multiple_inheritance;
py::class_<CppBase>(m, "CppBase")
.def(py::init<int>())
.def("get_base_value", &CppBase::get_base_value)
.def("reset_base_value", &CppBase::reset_base_value);
py::class_<CppDrvd, CppBase>(m, "CppDrvd")
.def(py::init<int>())
.def("get_drvd_value", &CppDrvd::get_drvd_value)
.def("reset_drvd_value", &CppDrvd::reset_drvd_value)
.def("get_base_value_from_drvd", &CppDrvd::get_base_value_from_drvd)
.def("reset_base_value_from_drvd", &CppDrvd::reset_base_value_from_drvd);
}

View File

@ -0,0 +1,35 @@
# Adapted from:
# https://github.com/google/clif/blob/5718e4d0807fd3b6a8187dde140069120b81ecef/clif/testing/python/python_multiple_inheritance_test.py
from pybind11_tests import python_multiple_inheritance as m
class PC(m.CppBase):
pass
class PPCC(PC, m.CppDrvd):
pass
def test_PC():
d = PC(11)
assert d.get_base_value() == 11
d.reset_base_value(13)
assert d.get_base_value() == 13
def test_PPCC():
d = PPCC(11)
assert d.get_drvd_value() == 33
d.reset_drvd_value(55)
assert d.get_drvd_value() == 55
assert d.get_base_value() == 11
assert d.get_base_value_from_drvd() == 11
d.reset_base_value(20)
assert d.get_base_value() == 20
assert d.get_base_value_from_drvd() == 20
d.reset_base_value_from_drvd(30)
assert d.get_base_value() == 30
assert d.get_base_value_from_drvd() == 30

View File

@ -7,6 +7,8 @@
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/typing.h>
#include "pybind11_tests.h"
#include <utility>
@ -23,7 +25,7 @@ PyObject *conv(PyObject *o) {
ret = PyFloat_FromDouble(v);
}
} else {
PyErr_SetString(PyExc_TypeError, "Unexpected type");
py::set_error(PyExc_TypeError, "Unexpected type");
}
return ret;
}
@ -39,6 +41,15 @@ class float_ : public py::object {
};
} // namespace external
namespace pybind11 {
namespace detail {
template <>
struct handle_type_name<external::float_> {
static constexpr auto name = const_name("float");
};
} // namespace detail
} // namespace pybind11
namespace implicit_conversion_from_0_to_handle {
// Uncomment to trigger compiler error. Note: Before PR #4008 this used to compile successfully.
// void expected_to_trigger_compiler_error() { py::handle(0); }
@ -820,4 +831,16 @@ TEST_SUBMODULE(pytypes, m) {
a >>= b;
return a;
});
m.def("annotate_tuple_float_str", [](const py::typing::Tuple<py::float_, py::str> &) {});
m.def("annotate_tuple_empty", [](const py::typing::Tuple<> &) {});
m.def("annotate_tuple_variable_length",
[](const py::typing::Tuple<py::float_, py::ellipsis> &) {});
m.def("annotate_dict_str_int", [](const py::typing::Dict<py::str, int> &) {});
m.def("annotate_list_int", [](const py::typing::List<int> &) {});
m.def("annotate_set_str", [](const py::typing::Set<std::string> &) {});
m.def("annotate_iterable_str", [](const py::typing::Iterable<std::string> &) {});
m.def("annotate_iterator_int", [](const py::typing::Iterator<int> &) {});
m.def("annotate_fn",
[](const py::typing::Callable<int(py::typing::List<py::str>, py::str)> &) {});
}

View File

@ -121,7 +121,7 @@ def test_set(capture, doc):
assert m.anyset_contains({"foo"}, "foo")
assert doc(m.get_set) == "get_set() -> set"
assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None"
assert doc(m.print_anyset) == "print_anyset(arg0: Union[set, frozenset]) -> None"
def test_frozenset(capture, doc):
@ -896,3 +896,59 @@ def test_inplace_lshift(a, b):
def test_inplace_rshift(a, b):
expected = a >> b
assert m.inplace_rshift(a, b) == expected
def test_tuple_nonempty_annotations(doc):
assert (
doc(m.annotate_tuple_float_str)
== "annotate_tuple_float_str(arg0: tuple[float, str]) -> None"
)
def test_tuple_empty_annotations(doc):
assert (
doc(m.annotate_tuple_empty) == "annotate_tuple_empty(arg0: tuple[()]) -> None"
)
def test_tuple_variable_length_annotations(doc):
assert (
doc(m.annotate_tuple_variable_length)
== "annotate_tuple_variable_length(arg0: tuple[float, ...]) -> None"
)
def test_dict_annotations(doc):
assert (
doc(m.annotate_dict_str_int)
== "annotate_dict_str_int(arg0: dict[str, int]) -> None"
)
def test_list_annotations(doc):
assert doc(m.annotate_list_int) == "annotate_list_int(arg0: list[int]) -> None"
def test_set_annotations(doc):
assert doc(m.annotate_set_str) == "annotate_set_str(arg0: set[str]) -> None"
def test_iterable_annotations(doc):
assert (
doc(m.annotate_iterable_str)
== "annotate_iterable_str(arg0: Iterable[str]) -> None"
)
def test_iterator_annotations(doc):
assert (
doc(m.annotate_iterator_int)
== "annotate_iterator_int(arg0: Iterator[int]) -> None"
)
def test_fn_annotations(doc):
assert (
doc(m.annotate_fn)
== "annotate_fn(arg0: Callable[[list[str], str], int]) -> None"
)

View File

@ -28,6 +28,13 @@ class NonZeroIterator {
public:
explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {}
// Make the iterator non-copyable and movable
NonZeroIterator(const NonZeroIterator &) = delete;
NonZeroIterator(NonZeroIterator &&) noexcept = default;
NonZeroIterator &operator=(const NonZeroIterator &) = delete;
NonZeroIterator &operator=(NonZeroIterator &&) noexcept = default;
const T &operator*() const { return *ptr_; }
NonZeroIterator &operator++() {
++ptr_;
@ -78,6 +85,7 @@ private:
int value_;
};
using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>);
PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>);
@ -375,6 +383,17 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
private:
std::vector<std::pair<int, int>> data_;
};
{
// #4383 : Make sure `py::make_*iterator` functions work with move-only iterators
using iterator_t = NonZeroIterator<std::pair<int, int>>;
static_assert(std::is_move_assignable<iterator_t>::value, "");
static_assert(std::is_move_constructible<iterator_t>::value, "");
static_assert(!std::is_copy_assignable<iterator_t>::value, "");
static_assert(!std::is_copy_constructible<iterator_t>::value, "");
}
py::class_<IntPairs>(m, "IntPairs")
.def(py::init<std::vector<std::pair<int, int>>>())
.def(

View File

@ -58,6 +58,15 @@ def test_generalized_iterators_simple():
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_values()) == [2, 4, 5]
def test_iterator_doc_annotations():
assert m.IntPairs.nonref.__doc__.endswith("-> Iterator[tuple[int, int]]\n")
assert m.IntPairs.nonref_keys.__doc__.endswith("-> Iterator[int]\n")
assert m.IntPairs.nonref_values.__doc__.endswith("-> Iterator[int]\n")
assert m.IntPairs.simple_iterator.__doc__.endswith("-> Iterator[tuple[int, int]]\n")
assert m.IntPairs.simple_keys.__doc__.endswith("-> Iterator[int]\n")
assert m.IntPairs.simple_values.__doc__.endswith("-> Iterator[int]\n")
def test_iterator_referencing():
"""Test that iterators reference rather than copy their referents."""
vec = m.VectorNonCopyableInt()
@ -171,6 +180,10 @@ def test_sequence_length():
assert m.sequence_length("hello") == 5
def test_sequence_doc():
assert m.sequence_length.__doc__.strip() == "sequence_length(arg0: Sequence) -> int"
def test_map_iterator():
sm = m.StringMap({"hi": "bye", "black": "white"})
assert sm["hi"] == "bye"

View File

@ -103,21 +103,26 @@ private:
int value;
};
template <typename T>
std::unordered_set<T *> &pointer_set() {
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
static auto singleton = new std::unordered_set<T *>();
return *singleton;
}
// test_unique_nodelete
// Object with a private destructor
class MyObject4;
std::unordered_set<MyObject4 *> myobject4_instances;
class MyObject4 {
public:
explicit MyObject4(int value) : value{value} {
print_created(this);
myobject4_instances.insert(this);
pointer_set<MyObject4>().insert(this);
}
int value;
static void cleanupAllInstances() {
auto tmp = std::move(myobject4_instances);
myobject4_instances.clear();
auto tmp = std::move(pointer_set<MyObject4>());
pointer_set<MyObject4>().clear();
for (auto *o : tmp) {
delete o;
}
@ -125,7 +130,7 @@ public:
private:
~MyObject4() {
myobject4_instances.erase(this);
pointer_set<MyObject4>().erase(this);
print_destroyed(this);
}
};
@ -133,19 +138,17 @@ private:
// test_unique_deleter
// Object with std::unique_ptr<T, D> where D is not matching the base class
// Object with a protected destructor
class MyObject4a;
std::unordered_set<MyObject4a *> myobject4a_instances;
class MyObject4a {
public:
explicit MyObject4a(int i) : value{i} {
print_created(this);
myobject4a_instances.insert(this);
pointer_set<MyObject4a>().insert(this);
};
int value;
static void cleanupAllInstances() {
auto tmp = std::move(myobject4a_instances);
myobject4a_instances.clear();
auto tmp = std::move(pointer_set<MyObject4a>());
pointer_set<MyObject4a>().clear();
for (auto *o : tmp) {
delete o;
}
@ -153,7 +156,7 @@ public:
protected:
virtual ~MyObject4a() {
myobject4a_instances.erase(this);
pointer_set<MyObject4a>().erase(this);
print_destroyed(this);
}
};

View File

@ -16,8 +16,8 @@ def test_vector(doc):
assert m.load_bool_vector([True, False])
assert m.load_bool_vector((True, False))
assert doc(m.cast_vector) == "cast_vector() -> List[int]"
assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool"
assert doc(m.cast_vector) == "cast_vector() -> list[int]"
assert doc(m.load_vector) == "load_vector(arg0: list[int]) -> bool"
# Test regression caused by 936: pointers to stl containers weren't castable
assert m.cast_ptr_vector() == ["lvalue", "lvalue"]
@ -39,10 +39,10 @@ def test_array(doc):
assert m.load_array(lst)
assert m.load_array(tuple(lst))
assert doc(m.cast_array) == "cast_array() -> Annotated[List[int], FixedSize(2)]"
assert doc(m.cast_array) == "cast_array() -> Annotated[list[int], FixedSize(2)]"
assert (
doc(m.load_array)
== "load_array(arg0: Annotated[List[int], FixedSize(2)]) -> bool"
== "load_array(arg0: Annotated[list[int], FixedSize(2)]) -> bool"
)
@ -53,8 +53,8 @@ def test_valarray(doc):
assert m.load_valarray(lst)
assert m.load_valarray(tuple(lst))
assert doc(m.cast_valarray) == "cast_valarray() -> List[int]"
assert doc(m.load_valarray) == "load_valarray(arg0: List[int]) -> bool"
assert doc(m.cast_valarray) == "cast_valarray() -> list[int]"
assert doc(m.load_valarray) == "load_valarray(arg0: list[int]) -> bool"
def test_map(doc):
@ -66,8 +66,8 @@ def test_map(doc):
assert "key2" in d
assert m.load_map(d)
assert doc(m.cast_map) == "cast_map() -> Dict[str, str]"
assert doc(m.load_map) == "load_map(arg0: Dict[str, str]) -> bool"
assert doc(m.cast_map) == "cast_map() -> dict[str, str]"
assert doc(m.load_map) == "load_map(arg0: dict[str, str]) -> bool"
def test_set(doc):
@ -78,8 +78,8 @@ def test_set(doc):
assert m.load_set(s)
assert m.load_set(frozenset(s))
assert doc(m.cast_set) == "cast_set() -> Set[str]"
assert doc(m.load_set) == "load_set(arg0: Set[str]) -> bool"
assert doc(m.cast_set) == "cast_set() -> set[str]"
assert doc(m.load_set) == "load_set(arg0: set[str]) -> bool"
def test_recursive_casting():
@ -303,7 +303,7 @@ def test_stl_pass_by_pointer(msg):
msg(excinfo.value)
== """
stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported:
1. (v: List[int] = None) -> List[int]
1. (v: list[int] = None) -> list[int]
Invoked with:
"""
@ -315,7 +315,7 @@ def test_stl_pass_by_pointer(msg):
msg(excinfo.value)
== """
stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported:
1. (v: List[int] = None) -> List[int]
1. (v: list[int] = None) -> list[int]
Invoked with: None
"""

View File

@ -15,6 +15,7 @@
#include <deque>
#include <map>
#include <unordered_map>
#include <vector>
class El {
public:
@ -83,6 +84,71 @@ struct RecursiveMap : std::map<int, RecursiveMap> {
using Parent::Parent;
};
class UserVectorLike : private std::vector<int> {
public:
// This is only a subset of the member functions, as needed at the time.
using Base = std::vector<int>;
using typename Base::const_iterator;
using typename Base::difference_type;
using typename Base::iterator;
using typename Base::size_type;
using typename Base::value_type;
using Base::at;
using Base::back;
using Base::Base;
using Base::begin;
using Base::cbegin;
using Base::cend;
using Base::clear;
using Base::empty;
using Base::end;
using Base::erase;
using Base::front;
using Base::insert;
using Base::pop_back;
using Base::push_back;
using Base::reserve;
using Base::shrink_to_fit;
using Base::swap;
using Base::operator[];
using Base::capacity;
using Base::size;
};
bool operator==(UserVectorLike const &, UserVectorLike const &) { return true; }
bool operator!=(UserVectorLike const &, UserVectorLike const &) { return false; }
class UserMapLike : private std::map<int, int> {
public:
// This is only a subset of the member functions, as needed at the time.
using Base = std::map<int, int>;
using typename Base::const_iterator;
using typename Base::iterator;
using typename Base::key_type;
using typename Base::mapped_type;
using typename Base::size_type;
using typename Base::value_type;
using Base::at;
using Base::Base;
using Base::begin;
using Base::cbegin;
using Base::cend;
using Base::clear;
using Base::emplace;
using Base::emplace_hint;
using Base::empty;
using Base::end;
using Base::erase;
using Base::find;
using Base::insert;
using Base::max_size;
using Base::swap;
using Base::operator[];
using Base::size;
};
/*
* Pybind11 does not catch more complicated recursion schemes, such as mutual
* recursion.
@ -126,6 +192,16 @@ TEST_SUBMODULE(stl_binders, m) {
py::bind_map<std::unordered_map<std::string, double const>>(m,
"UnorderedMapStringDoubleConst");
// test_map_view_types
py::bind_map<std::map<std::string, float>>(m, "MapStringFloat");
py::bind_map<std::unordered_map<std::string, float>>(m, "UnorderedMapStringFloat");
py::bind_map<std::map<std::pair<double, int>, int32_t>>(m, "MapPairDoubleIntInt32");
py::bind_map<std::map<std::pair<double, int>, int64_t>>(m, "MapPairDoubleIntInt64");
py::bind_map<std::map<int, py::object>>(m, "MapIntObject");
py::bind_map<std::map<std::string, py::object>>(m, "MapStringObject");
py::class_<E_nc>(m, "ENC").def(py::init<int>()).def_readwrite("value", &E_nc::value);
// test_noncopyable_containers
@ -173,6 +249,10 @@ TEST_SUBMODULE(stl_binders, m) {
py::bind_map<MutuallyRecursiveContainerPairMV>(m, "MutuallyRecursiveContainerPairMV");
py::bind_vector<MutuallyRecursiveContainerPairVM>(m, "MutuallyRecursiveContainerPairVM");
// Bind with private inheritance + `using` directives.
py::bind_vector<UserVectorLike>(m, "UserVectorLike");
py::bind_map<UserMapLike>(m, "UserMapLike");
// The rest depends on numpy:
try {
py::module_::import("numpy");

View File

@ -209,7 +209,7 @@ def test_map_string_double_const():
def test_noncopyable_containers():
# std::vector
vnc = m.get_vnc(5)
for i in range(0, 5):
for i in range(5):
assert vnc[i].value == i + 1
for i, j in enumerate(vnc, start=1):
@ -217,7 +217,7 @@ def test_noncopyable_containers():
# std::deque
dnc = m.get_dnc(5)
for i in range(0, 5):
for i in range(5):
assert dnc[i].value == i + 1
i = 1
@ -252,7 +252,7 @@ def test_noncopyable_containers():
# nested std::map<std::vector>
nvnc = m.get_nvnc(5)
for i in range(1, 6):
for j in range(0, 5):
for j in range(5):
assert nvnc[i][j].value == j + 1
# Note: maps do not have .values()
@ -317,9 +317,9 @@ def test_map_view_types():
map_string_double_const = m.MapStringDoubleConst()
unordered_map_string_double_const = m.UnorderedMapStringDoubleConst()
assert map_string_double.keys().__class__.__name__ == "KeysView[str]"
assert map_string_double.values().__class__.__name__ == "ValuesView[float]"
assert map_string_double.items().__class__.__name__ == "ItemsView[str, float]"
assert map_string_double.keys().__class__.__name__ == "KeysView"
assert map_string_double.values().__class__.__name__ == "ValuesView"
assert map_string_double.items().__class__.__name__ == "ItemsView"
keys_type = type(map_string_double.keys())
assert type(unordered_map_string_double.keys()) is keys_type
@ -336,6 +336,30 @@ def test_map_view_types():
assert type(map_string_double_const.items()) is items_type
assert type(unordered_map_string_double_const.items()) is items_type
map_string_float = m.MapStringFloat()
unordered_map_string_float = m.UnorderedMapStringFloat()
assert type(map_string_float.keys()) is keys_type
assert type(unordered_map_string_float.keys()) is keys_type
assert type(map_string_float.values()) is values_type
assert type(unordered_map_string_float.values()) is values_type
assert type(map_string_float.items()) is items_type
assert type(unordered_map_string_float.items()) is items_type
map_pair_double_int_int32 = m.MapPairDoubleIntInt32()
map_pair_double_int_int64 = m.MapPairDoubleIntInt64()
assert type(map_pair_double_int_int32.values()) is values_type
assert type(map_pair_double_int_int64.values()) is values_type
map_int_object = m.MapIntObject()
map_string_object = m.MapStringObject()
assert type(map_int_object.keys()) is keys_type
assert type(map_string_object.keys()) is keys_type
assert type(map_int_object.items()) is items_type
assert type(map_string_object.items()) is items_type
def test_recursive_vector():
recursive_vector = m.RecursiveVector()
@ -353,3 +377,17 @@ def test_recursive_map():
recursive_map[100][101] = m.RecursiveMap()
recursive_map[100][102] = m.RecursiveMap()
assert list(recursive_map[100].keys()) == [101, 102]
def test_user_vector_like():
vec = m.UserVectorLike()
vec.append(2)
assert vec[0] == 2
assert len(vec) == 1
def test_user_like_map():
map = m.UserMapLike()
map[33] = 44
assert map[33] == 44
assert len(map) == 1

View File

@ -78,14 +78,14 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
m.def("cast_to_pyobject_ptr_nullptr", [](bool set_error) {
if (set_error) {
PyErr_SetString(PyExc_RuntimeError, "Reflective of healthy error handling.");
py::set_error(PyExc_RuntimeError, "Reflective of healthy error handling.");
}
PyObject *ptr = nullptr;
py::cast(ptr);
});
m.def("cast_to_pyobject_ptr_non_nullptr_with_error_set", []() {
PyErr_SetString(PyExc_RuntimeError, "Reflective of unhealthy error handling.");
py::set_error(PyExc_RuntimeError, "Reflective of unhealthy error handling.");
py::cast(Py_None);
});

View File

@ -95,6 +95,22 @@ if(NOT PythonLibsNew_FIND_VERSION)
set(PythonLibsNew_FIND_VERSION "3.6")
endif()
if(NOT CMAKE_VERSION VERSION_LESS "3.27")
cmake_policy(GET CMP0148 _pybind11_cmp0148)
if(NOT _pybind11_cmp0148)
message(
AUTHOR_WARNING
"Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs "
"modules are removed. Run \"cmake --help-policy CMP0148\" for policy "
"details. Use the cmake_policy command to set the policy and suppress "
"this warning, or preferably upgrade to using FindPython, either by "
"calling it explicitly before pybind11, or by setting "
"PYBIND11_FINDPYTHON ON before pybind11.")
endif()
cmake_policy(SET CMP0148 OLD)
unset(_pybind11_cmp0148)
endif()
find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required}
${_pythonlibs_quiet})
@ -172,13 +188,20 @@ _pybind11_get_if_undef(_PYTHON_VALUES 0 _PYTHON_VERSION_LIST)
_pybind11_get_if_undef(_PYTHON_VALUES 1 PYTHON_PREFIX)
_pybind11_get_if_undef(_PYTHON_VALUES 2 PYTHON_INCLUDE_DIR)
_pybind11_get_if_undef(_PYTHON_VALUES 3 PYTHON_SITE_PACKAGES)
_pybind11_get_if_undef(_PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION)
_pybind11_get_if_undef(_PYTHON_VALUES 5 PYTHON_IS_DEBUG)
_pybind11_get_if_undef(_PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P)
_pybind11_get_if_undef(_PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX)
_pybind11_get_if_undef(_PYTHON_VALUES 8 PYTHON_LIBDIR)
_pybind11_get_if_undef(_PYTHON_VALUES 9 PYTHON_MULTIARCH)
list(GET _PYTHON_VALUES 4 _PYTHON_MODULE_EXT_SUFFIX)
if(PYBIND11_PYTHONLIBS_OVERWRITE OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX)
get_filename_component(PYTHON_MODULE_DEBUG_POSTFIX "${_PYTHON_MODULE_EXT_SUFFIX}" NAME_WE)
endif()
if(PYBIND11_PYTHONLIBS_OVERWRITE OR NOT DEFINED PYTHON_MODULE_EXTENSION)
get_filename_component(PYTHON_MODULE_EXTENSION "${_PYTHON_MODULE_EXT_SUFFIX}" EXT)
endif()
# Make sure the Python has the same pointer-size as the chosen compiler
# Skip if CMAKE_SIZEOF_VOID_P is not defined
# This should be skipped for (non-Apple) cross-compiles (like EMSCRIPTEN)

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
@ -29,6 +30,18 @@ issues_pages = ghapi.page.paged(
)
issues = (issue for page in issues_pages for issue in page)
missing = []
cats_descr = {
"feat": "New Features",
"fix": "Bug fixes",
"fix(types)": "",
"fix(cmake)": "",
"docs": "Documentation",
"tests": "Tests",
"ci": "CI",
"chore": "Other",
"unknown": "Uncategorised",
}
cats: dict[str, list[str]] = {c: [] for c in cats_descr}
for issue in issues:
changelog = ENTRY.findall(issue.body or "")
@ -36,15 +49,30 @@ for issue in issues:
missing.append(issue)
else:
(msg,) = changelog
if msg.startswith("- "):
msg = msg[2:]
if not msg.startswith("* "):
msg = "* " + msg
if not msg.endswith("."):
msg += "."
msg += f"\n `#{issue.number} <{issue.html_url}>`_"
for cat in cats:
if issue.title.lower().startswith(f"{cat}:"):
cats[cat].append(msg)
break
else:
cats["unknown"].append(msg)
for cat, msgs in cats.items():
if msgs:
desc = cats_descr[cat]
print(f"[bold]{desc}:" if desc else f".. {cat}")
print()
for msg in msgs:
print(Syntax(msg, "rst", theme="ansi_light", word_wrap=True))
print()
print()
if missing:
print()

View File

@ -173,12 +173,16 @@ endif()
# Check to see which Python mode we are in, new, old, or no python
if(PYBIND11_NOPYTHON)
set(_pybind11_nopython ON)
# We won't use new FindPython if PYBIND11_FINDPYTHON is defined and falselike
# Otherwise, we use if FindPythonLibs is missing or if FindPython was already used
elseif(
_pybind11_missing_old_python STREQUAL "NEW"
(NOT DEFINED PYBIND11_FINDPYTHON OR PYBIND11_FINDPYTHON)
AND (_pybind11_missing_old_python STREQUAL "NEW"
OR PYBIND11_FINDPYTHON
OR Python_FOUND
OR Python2_FOUND
OR Python3_FOUND)
OR Python3_FOUND
))
# New mode
include("${CMAKE_CURRENT_LIST_DIR}/pybind11NewTools.cmake")
@ -218,8 +222,15 @@ if(NOT _pybind11_nopython)
execute_process(
COMMAND
${${_Python}_EXECUTABLE} -c
"from pkg_resources import get_distribution; print(get_distribution('${PYPI_NAME}').version)"
${${_Python}_EXECUTABLE} -c "
try:
from importlib.metadata import version
except ImportError:
from pkg_resources import get_distribution
def version(s):
return get_distribution(s).version
print(version('${PYPI_NAME}'))
"
RESULT_VARIABLE RESULT_PRESENT
OUTPUT_VARIABLE PKG_VERSION
ERROR_QUIET)
@ -300,21 +311,24 @@ function(_pybind11_generate_lto target prefer_thin_lto)
set(cxx_append ";-fno-fat-lto-objects")
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
set(NO_FLTO_ARCH TRUE)
if(prefer_thin_lto)
set(thin "=thin")
else()
set(NO_FLTO_ARCH FALSE)
set(thin "")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang"
AND prefer_thin_lto
AND NOT NO_FLTO_ARCH)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
# Do nothing
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES emscripten)
# This compile is very costly when cross-compiling, so set this without checking
set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}")
set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
_pybind11_return_if_cxx_and_linker_flags_work(
HAS_FLTO_THIN "-flto=thin${cxx_append}" "-flto=thin${linker_append}"
HAS_FLTO_THIN "-flto${thin}${cxx_append}" "-flto=${thin}${linker_append}"
PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS)
endif()
if(NOT HAS_FLTO_THIN AND NOT NO_FLTO_ARCH)
if(NOT HAS_FLTO_THIN)
_pybind11_return_if_cxx_and_linker_flags_work(
HAS_FLTO "-flto${cxx_append}" "-flto${linker_append}" PYBIND11_LTO_CXX_FLAGS
PYBIND11_LTO_LINKER_FLAGS)

View File

@ -149,7 +149,7 @@ default is ``MODULE``. There are several options:
``OPT_SIZE``
Optimize for size, even if the ``CMAKE_BUILD_TYPE`` is not ``MinSizeRel``.
``THIN_LTO``
Use thin TLO instead of regular if there's a choice (pybind11's selection
Use thin LTO instead of regular if there's a choice (pybind11's selection
is disabled if ``CMAKE_INTERPROCEDURAL_OPTIMIZATIONS`` is set).
``WITHOUT_SOABI``
Disable the SOABI component (``PYBIND11_NEWPYTHON`` mode only).

View File

@ -32,17 +32,54 @@ if(NOT Python_FOUND AND NOT Python3_FOUND)
set(Python_ROOT_DIR "$ENV{pythonLocation}")
endif()
find_package(Python 3.6 REQUIRED COMPONENTS Interpreter Development ${_pybind11_quiet})
# Development.Module support (required for manylinux) started in 3.18
if(CMAKE_VERSION VERSION_LESS 3.18)
set(_pybind11_dev_component Development)
else()
set(_pybind11_dev_component Development.Module OPTIONAL_COMPONENTS Development.Embed)
endif()
# Callers need to be able to access Python_EXECUTABLE
set(_pybind11_global_keyword "")
if(NOT is_config AND NOT DEFINED Python_ARTIFACTS_INTERACTIVE)
set(Python_ARTIFACTS_INTERACTIVE TRUE)
if(NOT CMAKE_VERSION VERSION_LESS 3.24)
set(_pybind11_global_keyword "GLOBAL")
endif()
endif()
find_package(Python 3.6 REQUIRED COMPONENTS Interpreter ${_pybind11_dev_component}
${_pybind11_quiet} ${_pybind11_global_keyword})
# If we are in submodule mode, export the Python targets to global targets.
# If this behavior is not desired, FindPython _before_ pybind11.
if(NOT is_config)
if(NOT is_config
AND Python_ARTIFACTS_INTERACTIVE
AND _pybind11_global_keyword STREQUAL "")
if(TARGET Python::Python)
set_property(TARGET Python::Python PROPERTY IMPORTED_GLOBAL TRUE)
endif()
set_property(TARGET Python::Interpreter PROPERTY IMPORTED_GLOBAL TRUE)
if(TARGET Python::Module)
set_property(TARGET Python::Module PROPERTY IMPORTED_GLOBAL TRUE)
endif()
endif()
# Explicitly export version for callers (including our own functions)
if(NOT is_config AND Python_ARTIFACTS_INTERACTIVE)
set(Python_VERSION
"${Python_VERSION}"
CACHE INTERNAL "")
set(Python_VERSION_MAJOR
"${Python_VERSION_MAJOR}"
CACHE INTERNAL "")
set(Python_VERSION_MINOR
"${Python_VERSION_MINOR}"
CACHE INTERNAL "")
set(Python_VERSION_PATCH
"${Python_VERSION_PATCH}"
CACHE INTERNAL "")
endif()
endif()
if(Python_FOUND)
@ -73,14 +110,16 @@ if(NOT DEFINED ${_Python}_EXECUTABLE)
endif()
if(NOT ${_Python}_EXECUTABLE STREQUAL PYBIND11_PYTHON_EXECUTABLE_LAST)
if(DEFINED PYBIND11_PYTHON_EXECUTABLE_LAST AND NOT ${_Python}_EXECUTABLE STREQUAL
PYBIND11_PYTHON_EXECUTABLE_LAST)
# Detect changes to the Python version/binary in subsequent CMake runs, and refresh config if needed
unset(PYTHON_IS_DEBUG CACHE)
unset(PYTHON_MODULE_EXTENSION CACHE)
endif()
set(PYBIND11_PYTHON_EXECUTABLE_LAST
"${${_Python}_EXECUTABLE}"
CACHE INTERNAL "Python executable during the last CMake run")
endif()
if(NOT DEFINED PYTHON_IS_DEBUG)
# Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter
@ -95,26 +134,37 @@ endif()
# Get the suffix - SO is deprecated, should use EXT_SUFFIX, but this is
# required for PyPy3 (as of 7.3.1)
if(NOT DEFINED PYTHON_MODULE_EXTENSION)
if(NOT DEFINED PYTHON_MODULE_EXTENSION OR NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX)
execute_process(
COMMAND
"${${_Python}_EXECUTABLE}" "-c"
"import sys, importlib; s = importlib.import_module('distutils.sysconfig' if sys.version_info < (3, 10) else 'sysconfig'); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO'))"
OUTPUT_VARIABLE _PYTHON_MODULE_EXTENSION
ERROR_VARIABLE _PYTHON_MODULE_EXTENSION_ERR
OUTPUT_VARIABLE _PYTHON_MODULE_EXT_SUFFIX
ERROR_VARIABLE _PYTHON_MODULE_EXT_SUFFIX_ERR
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(_PYTHON_MODULE_EXTENSION STREQUAL "")
if(_PYTHON_MODULE_EXT_SUFFIX STREQUAL "")
message(
FATAL_ERROR "pybind11 could not query the module file extension, likely the 'distutils'"
"package is not installed. Full error message:\n${_PYTHON_MODULE_EXTENSION_ERR}")
"package is not installed. Full error message:\n${_PYTHON_MODULE_EXT_SUFFIX_ERR}"
)
endif()
# This needs to be available for the pybind11_extension function
if(NOT DEFINED PYTHON_MODULE_DEBUG_POSTFIX)
get_filename_component(_PYTHON_MODULE_DEBUG_POSTFIX "${_PYTHON_MODULE_EXT_SUFFIX}" NAME_WE)
set(PYTHON_MODULE_DEBUG_POSTFIX
"${_PYTHON_MODULE_DEBUG_POSTFIX}"
CACHE INTERNAL "")
endif()
if(NOT DEFINED PYTHON_MODULE_EXTENSION)
get_filename_component(_PYTHON_MODULE_EXTENSION "${_PYTHON_MODULE_EXT_SUFFIX}" EXT)
set(PYTHON_MODULE_EXTENSION
"${_PYTHON_MODULE_EXTENSION}"
CACHE INTERNAL "")
endif()
endif()
# Python debug libraries expose slightly different objects before 3.8
# https://docs.python.org/3.6/c-api/intro.html#debugging-builds
@ -233,12 +283,14 @@ function(pybind11_add_module target_name)
endif()
endif()
if(DEFINED CMAKE_BUILD_TYPE) # see https://github.com/pybind/pybind11/issues/4454
# Use case-insensitive comparison to match the result of $<CONFIG:cfgs>
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO)
# Strip unnecessary sections of the binary on Linux/macOS
pybind11_strip(${target_name})
endif()
endif()
if(MSVC)
target_link_libraries(${target_name} PRIVATE pybind11::windows_extras)
@ -251,6 +303,9 @@ endfunction()
function(pybind11_extension name)
# The extension is precomputed
set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX "${PYTHON_MODULE_EXTENSION}")
set_target_properties(
${name}
PROPERTIES PREFIX ""
DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}"
SUFFIX "${PYTHON_MODULE_EXTENSION}")
endfunction()

View File

@ -43,7 +43,7 @@ endif()
# A user can set versions manually too
set(Python_ADDITIONAL_VERSIONS
"3.11;3.10;3.9;3.8;3.7;3.6"
"3.12;3.11;3.10;3.9;3.8;3.7;3.6"
CACHE INTERNAL "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
@ -65,6 +65,7 @@ _pybind11_promote_to_cache(PYTHON_INCLUDE_DIRS)
_pybind11_promote_to_cache(PYTHON_LIBRARIES)
_pybind11_promote_to_cache(PYTHON_MODULE_PREFIX)
_pybind11_promote_to_cache(PYTHON_MODULE_EXTENSION)
_pybind11_promote_to_cache(PYTHON_MODULE_DEBUG_POSTFIX)
_pybind11_promote_to_cache(PYTHON_VERSION_MAJOR)
_pybind11_promote_to_cache(PYTHON_VERSION_MINOR)
_pybind11_promote_to_cache(PYTHON_VERSION)
@ -148,7 +149,10 @@ endif()
function(pybind11_extension name)
# The prefix and extension are provided by FindPythonLibsNew.cmake
set_target_properties(${name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
set_target_properties(
${name}
PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}"
SUFFIX "${PYTHON_MODULE_EXTENSION}")
endfunction()
@ -212,11 +216,13 @@ function(pybind11_add_module target_name)
endif()
endif()
if(DEFINED CMAKE_BUILD_TYPE) # see https://github.com/pybind/pybind11/issues/4454
# Use case-insensitive comparison to match the result of $<CONFIG:cfgs>
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
if(NOT MSVC AND NOT "${uppercase_CMAKE_BUILD_TYPE}" MATCHES DEBUG|RELWITHDEBINFO)
pybind11_strip(${target_name})
endif()
endif()
if(MSVC)
target_link_libraries(${target_name} PRIVATE pybind11::windows_extras)