mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
Merge branch 'master' into stable
This commit is contained in:
commit
01ab935612
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -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.
|
||||
|
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -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"
|
||||
|
185
.github/workflows/ci.yml
vendored
185
.github/workflows/ci.yml
vendored
@ -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: |
|
||||
|
6
.github/workflows/configure.yml
vendored
6
.github/workflows/configure.yml
vendored
@ -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 }}
|
||||
|
||||
|
8
.github/workflows/format.yml
vendored
8
.github/workflows/format.yml
vendored
@ -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
|
||||
|
2
.github/workflows/labeler.yml
vendored
2
.github/workflows/labeler.yml
vendored
@ -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):') &&
|
||||
|
16
.github/workflows/pip.yml
vendored
16
.github/workflows/pip.yml
vendored
@ -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
|
||||
|
14
.github/workflows/upstream.yml
vendored
14
.github/workflows/upstream.yml
vendored
@ -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: |
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}")
|
||||
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"
|
||||
|
@ -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
|
||||
-------
|
||||
|
@ -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)`
|
||||
|
@ -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...
|
||||
|
@ -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`.
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
128
docs/release.rst
128
docs/release.rst
@ -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,53 +26,93 @@ 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)
|
||||
- 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
|
||||
`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
|
||||
- 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 ``PYBIND11_VERSION_MAJOR`` etc. in
|
||||
``include/pybind11/detail/common.h``. PATCH should be a simple integer.
|
||||
|
||||
- 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 ``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
|
||||
|
||||
- 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``.
|
||||
non-annotated tag for you)
|
||||
|
||||
- ``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 push``
|
||||
|
||||
- ``git checkout stable``
|
||||
|
||||
- ``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``).
|
||||
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``.
|
||||
|
||||
- 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. 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
|
||||
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
|
||||
- Add a spot for in-development updates in ``docs/changelog.rst``.
|
||||
- ``git add``, ``git commit``, ``git push``
|
||||
|
||||
- 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 ``pybind11/_version.py`` to match.
|
||||
|
||||
- Run ``nox -s tests_packaging`` to ensure this was done correctly.
|
||||
|
||||
- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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{}); }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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; \
|
||||
}
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */,
|
||||
0, /* vpos: (non-simple types only): the first vptr comes first */
|
||||
0 /* index */) {}
|
||||
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 */);
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
|
@ -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) {
|
||||
|
@ -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("]"));
|
||||
};
|
||||
|
@ -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); }
|
||||
|
91
include/pybind11/gil_safe_call_once.h
Normal file
91
include/pybind11/gil_safe_call_once.h
Normal 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)
|
@ -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'.
|
||||
@ -618,20 +736,39 @@ public:
|
||||
/// Single character for byteorder
|
||||
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
|
||||
|
||||
/// Alignment of the data type
|
||||
/// 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
|
||||
/// 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,
|
||||
|
@ -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,9 +735,8 @@ protected:
|
||||
if (overloads->is_constructor) {
|
||||
if (!parent
|
||||
|| !PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) {
|
||||
PyErr_SetString(
|
||||
PyExc_TypeError,
|
||||
"__init__(self, ...) called with invalid or missing `self` argument");
|
||||
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 ? ®ister_local_exception_translator : ®ister_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);
|
||||
|
@ -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>;
|
||||
@ -305,19 +306,19 @@ private:
|
||||
"https://pybind11.readthedocs.io/en/stable/advanced/"
|
||||
"misc.html#common-sources-of-global-interpreter-lock-errors for debugging advice.\n"
|
||||
"If you are convinced there is no bug in your code, you can #define "
|
||||
"PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF"
|
||||
"PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF "
|
||||
"to disable this check. In that case you have to ensure this #define is consistently "
|
||||
"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
|
||||
|
@ -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("]"));
|
||||
};
|
||||
|
||||
|
@ -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 ↦
|
||||
};
|
||||
|
||||
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 ↦
|
||||
};
|
||||
|
||||
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
125
include/pybind11/typing.h
Normal 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)
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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("."))
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -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)
|
||||
|
@ -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}"
|
||||
)
|
||||
|
@ -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.
|
||||
|
@ -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 = {
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
"""
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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"
|
||||
|
@ -5,9 +5,8 @@ function(pybind11_add_build_test name)
|
||||
|
||||
set(build_options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
|
||||
|
||||
list(APPEND build_options "-DPYBIND11_FINDPYTHON=${PYBIND11_FINDPYTHON}")
|
||||
if(PYBIND11_FINDPYTHON)
|
||||
list(APPEND build_options "-DPYBIND11_FINDPYTHON=${PYBIND11_FINDPYTHON}")
|
||||
|
||||
if(DEFINED Python_ROOT_DIR)
|
||||
list(APPEND build_options "-DPython_ROOT_DIR=${Python_ROOT_DIR}")
|
||||
endif()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 &) {});
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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__")
|
||||
|
@ -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>(); });
|
||||
}
|
||||
|
@ -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()); }
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -232,25 +232,29 @@ 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; " + (
|
||||
"#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"
|
||||
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; " + (
|
||||
"#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"
|
||||
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"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -157,7 +157,7 @@ py::array mkarray_via_buffer(size_t n) {
|
||||
do { \
|
||||
(s).bool_ = (i) % 2 != 0; \
|
||||
(s).uint_ = (uint32_t) (i); \
|
||||
(s).float_ = (float) (i) *1.5f; \
|
||||
(s).float_ = (float) (i) * 1.5f; \
|
||||
(s).ldbl_ = (long double) (i) * -2.5L; \
|
||||
} while (0)
|
||||
|
||||
@ -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;
|
||||
|
@ -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):
|
||||
|
45
tests/test_python_multiple_inheritance.cpp
Normal file
45
tests/test_python_multiple_inheritance.cpp
Normal 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);
|
||||
}
|
35
tests/test_python_multiple_inheritance.py
Normal file
35
tests/test_python_multiple_inheritance.py
Normal 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
|
@ -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); }
|
||||
@ -660,8 +671,8 @@ TEST_SUBMODULE(pytypes, m) {
|
||||
// This is "most correct" and enforced on these platforms.
|
||||
# define PYBIND11_AUTO_IT auto it
|
||||
#else
|
||||
// This works on many platforms and is (unfortunately) reflective of existing user code.
|
||||
// NOLINTNEXTLINE(bugprone-macro-parentheses)
|
||||
// This works on many platforms and is (unfortunately) reflective of existing user code.
|
||||
// NOLINTNEXTLINE(bugprone-macro-parentheses)
|
||||
# define PYBIND11_AUTO_IT auto &it
|
||||
#endif
|
||||
|
||||
@ -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)> &) {});
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
"""
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,14 +49,29 @@ 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)
|
||||
|
||||
print(Syntax(msg, "rst", theme="ansi_light", word_wrap=True))
|
||||
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:
|
||||
|
@ -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"
|
||||
OR PYBIND11_FINDPYTHON
|
||||
OR Python_FOUND
|
||||
OR Python2_FOUND
|
||||
OR Python3_FOUND)
|
||||
(NOT DEFINED PYBIND11_FINDPYTHON OR PYBIND11_FINDPYTHON)
|
||||
AND (_pybind11_missing_old_python STREQUAL "NEW"
|
||||
OR PYBIND11_FINDPYTHON
|
||||
OR Python_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)
|
||||
|
@ -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).
|
||||
|
@ -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)
|
||||
set_property(TARGET Python::Python PROPERTY IMPORTED_GLOBAL TRUE)
|
||||
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,15 +110,17 @@ 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)
|
||||
set(PYBIND11_PYTHON_EXECUTABLE_LAST
|
||||
"${${_Python}_EXECUTABLE}"
|
||||
CACHE INTERNAL "Python executable during the last CMake run")
|
||||
endif()
|
||||
|
||||
set(PYBIND11_PYTHON_EXECUTABLE_LAST
|
||||
"${${_Python}_EXECUTABLE}"
|
||||
CACHE INTERNAL "Python executable during the last CMake run")
|
||||
|
||||
if(NOT DEFINED PYTHON_IS_DEBUG)
|
||||
# Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter
|
||||
execute_process(
|
||||
@ -95,25 +134,36 @@ 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
|
||||
set(PYTHON_MODULE_EXTENSION
|
||||
"${_PYTHON_MODULE_EXTENSION}"
|
||||
CACHE INTERNAL "")
|
||||
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
|
||||
@ -233,11 +283,13 @@ function(pybind11_add_module target_name)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# 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})
|
||||
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)
|
||||
@ -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()
|
||||
|
@ -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,8 +149,11 @@ endif()
|
||||
|
||||
function(pybind11_extension name)
|
||||
# The prefix and extension are provided by FindPythonLibsNew.cmake
|
||||
set_target_properties(${name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
|
||||
SUFFIX "${PYTHON_MODULE_EXTENSION}")
|
||||
set_target_properties(
|
||||
${name}
|
||||
PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
|
||||
DEBUG_POSTFIX "${PYTHON_MODULE_DEBUG_POSTFIX}"
|
||||
SUFFIX "${PYTHON_MODULE_EXTENSION}")
|
||||
endfunction()
|
||||
|
||||
# Build a Python extension module:
|
||||
@ -212,10 +216,12 @@ function(pybind11_add_module target_name)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# 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})
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user